From 56598f33defd9ade5a7c61d52bff9c8bcc22148e Mon Sep 17 00:00:00 2001 From: thosakwe Date: Sat, 3 Jun 2017 16:43:36 -0400 Subject: [PATCH] Github works --- .analysis-options | 2 + .gitignore | 46 ++++++++++- .../runConfigurations/Github_Auth_Server.xml | 7 ++ example/basic.dart | 55 ------------- example/github.dart | 81 +++++++++++++++++++ lib/angel_auth_oauth2.dart | 31 +++++-- pubspec.yaml | 13 +-- 7 files changed, 167 insertions(+), 68 deletions(-) create mode 100644 .analysis-options create mode 100644 .idea/runConfigurations/Github_Auth_Server.xml delete mode 100644 example/basic.dart create mode 100644 example/github.dart diff --git a/.analysis-options b/.analysis-options new file mode 100644 index 00000000..518eb901 --- /dev/null +++ b/.analysis-options @@ -0,0 +1,2 @@ +analyzer: + strong-mode: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2aa48381..a26910ac 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,48 @@ doc/api/ # (Library packages only! Remove pattern if developing an application package) pubspec.lock -log.txt \ No newline at end of file +log.txt +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff: +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/dictionaries + +# Sensitive or high-churn files: +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.xml +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml + +# Gradle: +.idea/**/gradle.xml +.idea/**/libraries + +# Mongo Explorer plugin: +.idea/**/mongoSettings.xml + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties diff --git a/.idea/runConfigurations/Github_Auth_Server.xml b/.idea/runConfigurations/Github_Auth_Server.xml new file mode 100644 index 00000000..c68cda20 --- /dev/null +++ b/.idea/runConfigurations/Github_Auth_Server.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/example/basic.dart b/example/basic.dart deleted file mode 100644 index cf3ed935..00000000 --- a/example/basic.dart +++ /dev/null @@ -1,55 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'package:angel_auth/angel_auth.dart'; -import 'package:angel_diagnostics/angel_diagnostics.dart'; -import 'package:angel_framework/angel_framework.dart'; -import 'package:angel_framework/common.dart'; -import 'package:angel_auth_oauth2/angel_auth_oauth2.dart'; -import 'package:oauth2/oauth2.dart' as oauth2; - -const Map OAUTH2_CONFIG = const { - 'callback': '', - 'key': '', - 'secret': '', - 'authorizationEndpoint': '', - 'tokenEndpoint': '' -}; - -main() async { - var app = new Angel()..use('/users', new TypedService(new MapService())); - - var auth = new AngelAuth(jwtKey: 'oauth2 example secret', allowCookie: false); - auth.deserializer = - (String idStr) => app.service('users').read(int.parse(idStr)); - auth.serializer = (User user) async => user.id; - - auth.strategies.add(new OAuth2Strategy('example_site', OAUTH2_CONFIG, - (oauth2.Client client) async { - var response = await client.get('/link/to/user/profile'); - return JSON.decode(response.body); - })); - - app.get('/auth/example_site', auth.authenticate('example_site')); - app.get( - '/auth/example_site/callback', - auth.authenticate('example_site', - new AngelAuthOptions(callback: (req, res, jwt) async { - // In real-life, you might include a pop-up callback script - res.write('Your JWT: $jwt'); - }))); - - await app.configure(auth); - await app.configure(logRequests(new File('log.txt'))); - await app.configure(profileRequests()); - - var server = await app.startServer(InternetAddress.LOOPBACK_IP_V4, 3000); - print('Listening on http://${server.address.address}:${server.port}'); -} - -class User extends Model { - String example_siteId; - - User({String id, this.example_siteId}) { - this.id = id; - } -} diff --git a/example/github.dart b/example/github.dart new file mode 100644 index 00000000..cb92b891 --- /dev/null +++ b/example/github.dart @@ -0,0 +1,81 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:angel_auth/angel_auth.dart'; +import 'package:angel_diagnostics/angel_diagnostics.dart'; +import 'package:angel_framework/angel_framework.dart'; +import 'package:angel_framework/common.dart'; +import 'package:angel_auth_oauth2/angel_auth_oauth2.dart'; +import 'package:oauth2/oauth2.dart' as oauth2; + +const AngelAuthOAuth2Options OAUTH2_CONFIG = const AngelAuthOAuth2Options( + callback: 'http://localhost:3000/auth/github/callback', + key: '6caeaf5d4c04936ec34f', + secret: '178360518cf9de4802e2346a4b6ebec525dc4427', + authorizationEndpoint: 'http://github.com/login/oauth/authorize', + tokenEndpoint: 'https://github.com/login/oauth/access_token'); + +main() async { + var app = new Angel(); + app.lazyParseBodies = true; + app.use('/users', new MapService()); + + var auth = new AngelAuth(jwtKey: 'oauth2 example secret', allowCookie: false); + + auth.deserializer = app.service('users').read; + auth.serializer = (User user) async => user.id; + + auth.strategies.add( + new OAuth2Strategy('github', OAUTH2_CONFIG, (oauth2.Client client) async { + var response = await client.get('https://api.github.com/user'); + var ghUser = JSON.decode(response.body); + var id = ghUser['id']; + + Iterable matchingUsers = await app.service('users').index({ + 'query': {'githubId': id} + }); + + if (matchingUsers.isNotEmpty) { + // Return the corresponding user, if it exists + return User.parse(matchingUsers.firstWhere((u) => u['githubId'] == id)); + } else { + // Otherwise,create a user + return await app + .service('users') + .create({'githubId': id}).then(User.parse); + } + })); + + app.get('/auth/github', auth.authenticate('github')); + app.get( + '/auth/github/callback', + auth.authenticate('github', + new AngelAuthOptions(callback: (req, res, jwt) async { + // In real-life, you might include a pop-up callback script. + // + // Use `confirmPopupAuthentication`, which is bundled with + // `package:angel_auth`. + res.write('Your JWT: $jwt'); + }))); + + await app.configure(auth); + await app.configure(logRequests()); + + var server = await app.startServer(InternetAddress.LOOPBACK_IP_V4, 3000); + var url = 'http://${server.address.address}:${server.port}'; + print('Listening on $url'); + print('View user listing: $url/users'); + print('Sign in via Github: $url/auth/github'); +} + +class User extends Model { + @override + String id; + int githubId; + + User({this.id, this.githubId}); + + static User parse(Map map) => + new User(id: map['id'], githubId: map['github_id']); + + Map toJson() => {'id': id, 'github_id': githubId}; +} diff --git a/lib/angel_auth_oauth2.dart b/lib/angel_auth_oauth2.dart index 0d29dfd2..226ef36a 100644 --- a/lib/angel_auth_oauth2.dart +++ b/lib/angel_auth_oauth2.dart @@ -23,19 +23,35 @@ final Validator OAUTH2_OPTIONS_SCHEMA = new Validator({ 'scopes': "'scopes' must be an Iterable of strings. You provided: {{value}}" }); +/// Holds credentials and also specifies the means of authenticating users against a remote server. class AngelAuthOAuth2Options { - String key, secret, authorizationEndpoint, tokenEndpoint, callback; - Iterable scopes; + /// Your application's client key or client ID, registered with the remote server. + final String key; - AngelAuthOAuth2Options( + /// Your application's client secret, registered with the remote server. + final String secret; + + /// The remote endpoint that prompts external users for authentication credentials. + final String authorizationEndpoint; + + /// The remote endpoint that exchanges auth codes for access tokens. + final String tokenEndpoint; + + /// The callback URL that the OAuth2 server should redirect authenticated users to. + final String callback; + + /// Used to split application scopes. Defaults to `' '`. + final String delimiter; + final Iterable scopes; + + const AngelAuthOAuth2Options( {this.key, this.secret, this.authorizationEndpoint, this.tokenEndpoint, this.callback, - Iterable scopes: const []}) { - this.scopes = scopes ?? []; - } + this.delimiter: ' ', + this.scopes: const []}); factory AngelAuthOAuth2Options.fromJson(Map json) => new AngelAuthOAuth2Options( @@ -85,7 +101,8 @@ class OAuth2Strategy implements AuthStrategy { _options.key, Uri.parse(_options.authorizationEndpoint), Uri.parse(_options.tokenEndpoint), - secret: _options.secret); + secret: _options.secret, + delimiter: _options.delimiter ?? ' '); @override Future authenticate(RequestContext req, ResponseContext res, diff --git a/pubspec.yaml b/pubspec.yaml index fcaae8ba..93b46603 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,11 +3,14 @@ description: angel_auth strategy for OAuth2 login, i.e. Facebook. version: 1.0.0 author: Tobe O environment: - sdk: ">=1.19.0" + sdk: ">=1.19.0" homepage: https://github.com/angel-dart/auth_oauth2.git dependencies: - angel_auth: ^1.0.0-dev - angel_validate: ^1.0.0-beta - oauth2: ^1.0.0 + angel_auth: ^1.0.0-dev + angel_validate: ^1.0.0-beta + oauth2: ^1.0.0 dev_dependencies: - angel_diagnostics: ^1.0.0-dev \ No newline at end of file + angel_diagnostics: ^1.0.0-dev +dependency_overrides: + oauth2: + git: https://github.com/thosakwe/oauth2.git \ No newline at end of file