Add 'packages/auth_oauth2/' from commit '97bd61e7ee6736c8a871d6cc45a5203e07bb03e6'
git-subtree-dir: packages/auth_oauth2 git-subtree-mainline:a584b8ce02
git-subtree-split:97bd61e7ee
This commit is contained in:
commit
c77d25f6d0
12 changed files with 512 additions and 0 deletions
75
packages/auth_oauth2/.gitignore
vendored
Normal file
75
packages/auth_oauth2/.gitignore
vendored
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
# See https://www.dartlang.org/tools/private-files.html
|
||||||
|
|
||||||
|
# Files and directories created by pub
|
||||||
|
.buildlog
|
||||||
|
.packages
|
||||||
|
.project
|
||||||
|
.pub/
|
||||||
|
build/
|
||||||
|
**/packages/
|
||||||
|
|
||||||
|
# Files created by dart2js
|
||||||
|
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||||
|
# rules if you intend to use dart2js directly
|
||||||
|
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
||||||
|
# differentiate from explicit Javascript files)
|
||||||
|
*.dart.js
|
||||||
|
*.part.js
|
||||||
|
*.js.deps
|
||||||
|
*.js.map
|
||||||
|
*.info.json
|
||||||
|
|
||||||
|
# Directory created by dartdoc
|
||||||
|
doc/api/
|
||||||
|
|
||||||
|
# Don't commit pubspec lock file
|
||||||
|
# (Library packages only! Remove pattern if developing an application package)
|
||||||
|
pubspec.lock
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
.dart_tool
|
16
packages/auth_oauth2/.idea/auth_oauth2.iml
Normal file
16
packages/auth_oauth2/.idea/auth_oauth2.iml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||||
|
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
8
packages/auth_oauth2/.idea/modules.xml
Normal file
8
packages/auth_oauth2/.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/auth_oauth2.iml" filepath="$PROJECT_DIR$/.idea/auth_oauth2.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Github Auth Server" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
||||||
|
<option name="filePath" value="$PROJECT_DIR$/example/main.dart" />
|
||||||
|
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||||
|
<method />
|
||||||
|
</configuration>
|
||||||
|
</component>
|
6
packages/auth_oauth2/.idea/vcs.xml
Normal file
6
packages/auth_oauth2/.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
13
packages/auth_oauth2/CHANGELOG.md
Normal file
13
packages/auth_oauth2/CHANGELOG.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# 2.1.0
|
||||||
|
* Angel 2 + Dart 2 update
|
||||||
|
* Support for handling errors + rejections.
|
||||||
|
* Use `ExternalAuthOptions`.
|
||||||
|
|
||||||
|
# 2.0.0+1
|
||||||
|
* Meta update to improve Pub score.
|
||||||
|
|
||||||
|
# 2.0.0
|
||||||
|
* Angel 2 + Dart 2 updates.
|
||||||
|
|
||||||
|
# 1.0.2
|
||||||
|
Added `getParameters` to `AngelOAuth2Options`.
|
21
packages/auth_oauth2/LICENSE
Normal file
21
packages/auth_oauth2/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Tobe O
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
133
packages/auth_oauth2/README.md
Normal file
133
packages/auth_oauth2/README.md
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
# auth_oauth2
|
||||||
|
|
||||||
|
[![Pub](https://img.shields.io/pub/v/angel_auth_oauth2.svg)](https://pub.dartlang.org/packages/angel_auth_oauth2)
|
||||||
|
|
||||||
|
`package:angel_auth` strategy for OAuth2 login, i.e. Facebook or Github.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
First, create an options object:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
configureServer(Angel app) async {
|
||||||
|
// Load from a Map, i.e. app config:
|
||||||
|
var opts = ExternalAuthOptions.fromMap(app.configuration['auth0'] as Map);
|
||||||
|
|
||||||
|
// Create in-place:
|
||||||
|
var opts = ExternalAuthOptions(
|
||||||
|
clientId: '<client-id>',
|
||||||
|
clientSecret: '<client-secret>',
|
||||||
|
redirectUri: Uri.parse('<callback>'));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After getting authenticated against the remote server, we need to be able to identify
|
||||||
|
users within our own application.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
typedef FutureOr<User> OAuth2Verifier(oauth2.Client, RequestContext, ResponseContext);
|
||||||
|
|
||||||
|
/// You might use a pure function to create a verifier that queries a
|
||||||
|
/// given service.
|
||||||
|
OAuth2Verifier oauth2verifier(Service<User> userService) {
|
||||||
|
return (client) async {
|
||||||
|
var response = await client.get('https://api.github.com/user');
|
||||||
|
var ghUser = json.decode(response.body);
|
||||||
|
var id = ghUser['id'] as int;
|
||||||
|
|
||||||
|
var matchingUsers = await mappedUserService.index({
|
||||||
|
'query': {'github_id': id}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchingUsers.isNotEmpty) {
|
||||||
|
// Return the corresponding user, if it exists.
|
||||||
|
return matchingUsers.first;
|
||||||
|
} else {
|
||||||
|
// Otherwise,create a user
|
||||||
|
return await mappedUserService.create(User(githubId: id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, initialize an `OAuth2Strategy`, using the options and verifier.
|
||||||
|
You'll also need to provide a name for this instance of the strategy.
|
||||||
|
Consider using the name of the remote authentication provider (ex. `facebook`).
|
||||||
|
|
||||||
|
```dart
|
||||||
|
configureServer(Angel app) {
|
||||||
|
auth.strategies['github'] = OAuth2Strategy(
|
||||||
|
options,
|
||||||
|
authorizationEndpoint,
|
||||||
|
tokenEndpoint,
|
||||||
|
yourVerifier,
|
||||||
|
|
||||||
|
// This function is called when an error occurs, or the user REJECTS the request.
|
||||||
|
(e, req, res) async {
|
||||||
|
res.write('Ooops: $e');
|
||||||
|
await res.close();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Lastly, connect it to an `AngelAuth` instance, and wire it up to an `Angel` server.
|
||||||
|
Set up two routes:
|
||||||
|
1. Redirect users to the external provider
|
||||||
|
2. Acts as a callback and handles an access code
|
||||||
|
|
||||||
|
In the case of the callback route, you may want to display an HTML page that closes
|
||||||
|
a popup window. In this case, use `confirmPopupAuthentication`, which is bundled with
|
||||||
|
`package:angel_auth`, as a `callback` function:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
configureServer(Angel app) async {
|
||||||
|
// ...
|
||||||
|
var auth = AngelAuth<User>();
|
||||||
|
auth.strategies['github'] = oauth2Strategy;
|
||||||
|
|
||||||
|
// Redirect
|
||||||
|
app.get('/auth/github', auth.authenticate('github'));
|
||||||
|
|
||||||
|
// Callback
|
||||||
|
app.get('/auth/github/callback', auth.authenticate(
|
||||||
|
'github',
|
||||||
|
AngelAuthOptions(callback: confirmPopupAuthentication())
|
||||||
|
));
|
||||||
|
|
||||||
|
// Connect the plug-in!!!
|
||||||
|
await app.configure(auth);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom Scope Delimiter
|
||||||
|
This package should work out-of-the-box for most OAuth2 providers, such as Github or Dropbox.
|
||||||
|
However, if your OAuth2 scopes are separated by a delimiter other than the default (`' '`),
|
||||||
|
you can add it in the `OAuth2Strategy` constructor:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
configureServer(Angel app) async {
|
||||||
|
OAuth2Strategy(..., delimiter: ' ');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Handling non-JSON responses
|
||||||
|
Many OAuth2 providers do not follow the specification, and do not return
|
||||||
|
`application/json` responses.
|
||||||
|
|
||||||
|
You can add a `getParameters` callback to parse the contents of any arbitrary
|
||||||
|
response:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
OAuth2Strategy(
|
||||||
|
// ...
|
||||||
|
getParameters: (contentType, body) {
|
||||||
|
if (contentType.type == 'application') {
|
||||||
|
if (contentType.subtype == 'x-www-form-urlencoded')
|
||||||
|
return Uri.splitQueryString(body);
|
||||||
|
else if (contentType.subtype == 'json') return JSON.decode(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw FormatException('Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
4
packages/auth_oauth2/analysis_options.yaml
Normal file
4
packages/auth_oauth2/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
include: package:pedantic/analysis_options.yaml
|
||||||
|
analyzer:
|
||||||
|
strong-mode:
|
||||||
|
implicit-casts: false
|
127
packages/auth_oauth2/example/main.dart
Normal file
127
packages/auth_oauth2/example/main.dart
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:angel_auth/angel_auth.dart';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:angel_framework/http.dart';
|
||||||
|
import 'package:angel_auth_oauth2/angel_auth_oauth2.dart';
|
||||||
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
var authorizationEndpoint =
|
||||||
|
Uri.parse('http://github.com/login/oauth/authorize');
|
||||||
|
|
||||||
|
var tokenEndpoint = Uri.parse('https://github.com/login/oauth/access_token');
|
||||||
|
|
||||||
|
var options = ExternalAuthOptions(
|
||||||
|
clientId: '6caeaf5d4c04936ec34f',
|
||||||
|
clientSecret: '178360518cf9de4802e2346a4b6ebec525dc4427',
|
||||||
|
redirectUri: Uri.parse('http://localhost:3000/auth/github/callback'),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Github doesn't properly follow the OAuth2 spec, so here's logic to parse their response.
|
||||||
|
Map<String, dynamic> parseParamsFromGithub(MediaType contentType, String body) {
|
||||||
|
if (contentType.type == 'application') {
|
||||||
|
if (contentType.subtype == 'x-www-form-urlencoded')
|
||||||
|
return Uri.splitQueryString(body);
|
||||||
|
else if (contentType.subtype == 'json')
|
||||||
|
return (json.decode(body) as Map).cast<String, String>();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw FormatException(
|
||||||
|
'Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
|
||||||
|
}
|
||||||
|
|
||||||
|
main() async {
|
||||||
|
// Create the server instance.
|
||||||
|
var app = Angel();
|
||||||
|
var http = AngelHttp(app);
|
||||||
|
app.logger = Logger('angel')
|
||||||
|
..onRecord.listen((rec) {
|
||||||
|
print(rec);
|
||||||
|
if (rec.error != null) print(rec.error);
|
||||||
|
if (rec.stackTrace != null) print(rec.stackTrace);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a service that stores user data.
|
||||||
|
var userService = app.use('/users', MapService()).inner;
|
||||||
|
var mappedUserService = userService.map(User.parse, User.serialize);
|
||||||
|
|
||||||
|
// Set up the authenticator plugin.
|
||||||
|
var auth =
|
||||||
|
AngelAuth<User>(jwtKey: 'oauth2 example secret', allowCookie: false);
|
||||||
|
auth.serializer = (user) async => user.id;
|
||||||
|
auth.deserializer = (id) => mappedUserService.read(id.toString());
|
||||||
|
app.fallback(auth.decodeJwt);
|
||||||
|
|
||||||
|
/// Create an instance of the strategy class.
|
||||||
|
auth.strategies['github'] = OAuth2Strategy(
|
||||||
|
options,
|
||||||
|
authorizationEndpoint,
|
||||||
|
tokenEndpoint,
|
||||||
|
|
||||||
|
// This function is called when the user ACCEPTS the request to sign in with Github.
|
||||||
|
(client, req, res) async {
|
||||||
|
var response = await client.get('https://api.github.com/user');
|
||||||
|
var ghUser = json.decode(response.body);
|
||||||
|
var id = ghUser['id'] as int;
|
||||||
|
|
||||||
|
var matchingUsers = await mappedUserService.index({
|
||||||
|
'query': {'github_id': id}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (matchingUsers.isNotEmpty) {
|
||||||
|
// Return the corresponding user, if it exists.
|
||||||
|
return matchingUsers.first;
|
||||||
|
} else {
|
||||||
|
// Otherwise,create a user
|
||||||
|
return await mappedUserService.create(User(githubId: id));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// This function is called when an error occurs, or the user REJECTS the request.
|
||||||
|
(e, req, res) async {
|
||||||
|
res.write('Ooops: $e');
|
||||||
|
await res.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
// We have to pass this parser function when working with Github.
|
||||||
|
getParameters: parseParamsFromGithub,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mount some routes
|
||||||
|
app.get('/auth/github', auth.authenticate('github'));
|
||||||
|
app.get(
|
||||||
|
'/auth/github/callback',
|
||||||
|
auth.authenticate('github',
|
||||||
|
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`.
|
||||||
|
var user = req.container.make<User>();
|
||||||
|
res.write('Your user info: ${user.toJson()}\n\n');
|
||||||
|
res.write('Your JWT: $jwt');
|
||||||
|
await res.close();
|
||||||
|
})));
|
||||||
|
|
||||||
|
// Start listening.
|
||||||
|
await http.startServer('127.0.0.1', 3000);
|
||||||
|
print('Listening on ${http.uri}');
|
||||||
|
print('View user listing: ${http.uri}/users');
|
||||||
|
print('Sign in via Github: ${http.uri}/auth/github');
|
||||||
|
}
|
||||||
|
|
||||||
|
class User extends Model {
|
||||||
|
@override
|
||||||
|
String id;
|
||||||
|
|
||||||
|
int githubId;
|
||||||
|
|
||||||
|
User({this.id, this.githubId});
|
||||||
|
|
||||||
|
static User parse(Map map) =>
|
||||||
|
User(id: map['id'] as String, githubId: map['github_id'] as int);
|
||||||
|
|
||||||
|
static Map<String, dynamic> serialize(User user) => user.toJson();
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {'id': id, 'github_id': githubId};
|
||||||
|
}
|
87
packages/auth_oauth2/lib/angel_auth_oauth2.dart
Normal file
87
packages/auth_oauth2/lib/angel_auth_oauth2.dart
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
library angel_auth_oauth2;
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:angel_auth/angel_auth.dart';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:http_parser/http_parser.dart';
|
||||||
|
import 'package:oauth2/oauth2.dart' as oauth2;
|
||||||
|
|
||||||
|
/// An Angel [AuthStrategy] that signs users in via a third-party service that speaks OAuth 2.0.
|
||||||
|
class OAuth2Strategy<User> implements AuthStrategy<User> {
|
||||||
|
/// A callback that uses the third-party service to authenticate a [User].
|
||||||
|
///
|
||||||
|
/// As always, return `null` if authentication fails.
|
||||||
|
final FutureOr<User> Function(oauth2.Client, RequestContext, ResponseContext)
|
||||||
|
verifier;
|
||||||
|
|
||||||
|
/// A callback that is triggered when an OAuth2 error occurs (i.e. the user declines to login);
|
||||||
|
final FutureOr<dynamic> Function(
|
||||||
|
oauth2.AuthorizationException, RequestContext, ResponseContext) onError;
|
||||||
|
|
||||||
|
/// The options defining how to connect to the third-party.
|
||||||
|
final ExternalAuthOptions options;
|
||||||
|
|
||||||
|
/// The URL to query to receive an authentication code.
|
||||||
|
final Uri authorizationEndpoint;
|
||||||
|
|
||||||
|
/// The URL to query to exchange an authentication code for a token.
|
||||||
|
final Uri tokenEndpoint;
|
||||||
|
|
||||||
|
/// An optional callback used to parse the response from a server who does not follow the OAuth 2.0 spec.
|
||||||
|
final Map<String, dynamic> Function(MediaType, String) getParameters;
|
||||||
|
|
||||||
|
/// An optional delimiter used to send requests to server who does not follow the OAuth 2.0 spec.
|
||||||
|
final String delimiter;
|
||||||
|
|
||||||
|
Uri _redirect;
|
||||||
|
|
||||||
|
OAuth2Strategy(this.options, this.authorizationEndpoint, this.tokenEndpoint,
|
||||||
|
this.verifier, this.onError,
|
||||||
|
{this.getParameters, this.delimiter = ' '});
|
||||||
|
|
||||||
|
oauth2.AuthorizationCodeGrant _createGrant() =>
|
||||||
|
new oauth2.AuthorizationCodeGrant(options.clientId, authorizationEndpoint,
|
||||||
|
tokenEndpoint,
|
||||||
|
secret: options.clientSecret,
|
||||||
|
delimiter: delimiter,
|
||||||
|
getParameters: getParameters);
|
||||||
|
|
||||||
|
@override
|
||||||
|
FutureOr<User> authenticate(RequestContext req, ResponseContext res,
|
||||||
|
[AngelAuthOptions<User> options]) async {
|
||||||
|
if (options != null) {
|
||||||
|
var result = await authenticateCallback(req, res, options);
|
||||||
|
if (result is User)
|
||||||
|
return result;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_redirect == null) {
|
||||||
|
var grant = _createGrant();
|
||||||
|
_redirect = grant.getAuthorizationUrl(
|
||||||
|
this.options.redirectUri,
|
||||||
|
scopes: this.options.scopes,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.redirect(_redirect);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The endpoint that is invoked by the third-party after successful authentication.
|
||||||
|
Future<dynamic> authenticateCallback(RequestContext req, ResponseContext res,
|
||||||
|
[AngelAuthOptions options]) async {
|
||||||
|
var grant = _createGrant();
|
||||||
|
grant.getAuthorizationUrl(this.options.redirectUri,
|
||||||
|
scopes: this.options.scopes);
|
||||||
|
|
||||||
|
try {
|
||||||
|
var client =
|
||||||
|
await grant.handleAuthorizationResponse(req.uri.queryParameters);
|
||||||
|
return await verifier(client, req, res);
|
||||||
|
} on oauth2.AuthorizationException catch (e) {
|
||||||
|
return await onError(e, req, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
packages/auth_oauth2/pubspec.yaml
Normal file
15
packages/auth_oauth2/pubspec.yaml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
name: angel_auth_oauth2
|
||||||
|
description: angel_auth strategy for OAuth2 login, i.e. Facebook, Github, etc.
|
||||||
|
version: 2.1.0
|
||||||
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.0.0-dev <3.0.0"
|
||||||
|
homepage: https://github.com/angel-dart/auth_oauth2.git
|
||||||
|
dependencies:
|
||||||
|
angel_auth: ^2.0.0
|
||||||
|
angel_framework: ^2.0.0-alpha
|
||||||
|
http_parser: ^3.0.0
|
||||||
|
oauth2: ^1.0.0
|
||||||
|
dev_dependencies:
|
||||||
|
logging: ^0.11.0
|
||||||
|
pedantic: ^1.0.0
|
Loading…
Reference in a new issue