2017-01-12 21:43:33 +00:00
|
|
|
# auth_oauth2
|
2017-02-23 01:13:23 +00:00
|
|
|
|
2018-03-30 16:44:15 +00:00
|
|
|
[![Pub](https://img.shields.io/pub/v/angel_auth_oauth2.svg)](https://pub.dartlang.org/packages/angel_auth_oauth2)
|
2017-01-12 22:48:51 +00:00
|
|
|
|
2017-06-03 21:05:13 +00:00
|
|
|
`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:
|
2019-01-06 00:43:06 +00:00
|
|
|
var opts = ExternalAuthOptions.fromMap(app.configuration['auth0'] as Map);
|
2017-06-03 21:05:13 +00:00
|
|
|
|
|
|
|
// Create in-place:
|
2019-01-06 00:43:06 +00:00
|
|
|
var opts = ExternalAuthOptions(
|
|
|
|
clientId: '<client-id>',
|
|
|
|
clientSecret: '<client-secret>',
|
|
|
|
redirectUri: Uri.parse('<callback>'));
|
2017-06-03 21:05:13 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
After getting authenticated against the remote server, we need to be able to identify
|
2019-01-06 00:43:06 +00:00
|
|
|
users within our own application.
|
2017-06-03 21:05:13 +00:00
|
|
|
|
|
|
|
```dart
|
2019-01-06 00:43:06 +00:00
|
|
|
typedef FutureOr<User> OAuth2Verifier(oauth2.Client, RequestContext, ResponseContext);
|
|
|
|
|
2017-06-03 21:05:13 +00:00
|
|
|
/// You might use a pure function to create a verifier that queries a
|
|
|
|
/// given service.
|
2019-01-06 00:43:06 +00:00
|
|
|
OAuth2Verifier oauth2verifier(Service<User> userService) {
|
|
|
|
return (client) async {
|
2017-06-03 21:05:13 +00:00
|
|
|
var response = await client.get('https://api.github.com/user');
|
2019-01-06 00:43:06 +00:00
|
|
|
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));
|
|
|
|
}
|
2017-06-03 21:05:13 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
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) {
|
2019-01-06 00:43:06 +00:00
|
|
|
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();
|
|
|
|
},
|
|
|
|
);
|
2017-06-03 21:05:13 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
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 {
|
|
|
|
// ...
|
2019-01-06 00:43:06 +00:00
|
|
|
var auth = AngelAuth<User>();
|
2018-09-12 03:23:42 +00:00
|
|
|
auth.strategies['github'] = oauth2Strategy;
|
2017-06-03 21:05:13 +00:00
|
|
|
|
|
|
|
// Redirect
|
|
|
|
app.get('/auth/github', auth.authenticate('github'));
|
|
|
|
|
|
|
|
// Callback
|
|
|
|
app.get('/auth/github/callback', auth.authenticate(
|
|
|
|
'github',
|
2019-01-06 00:43:06 +00:00
|
|
|
AngelAuthOptions(callback: confirmPopupAuthentication())
|
2017-06-03 21:05:13 +00:00
|
|
|
));
|
|
|
|
|
|
|
|
// 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 (`' '`),
|
2019-01-06 00:43:06 +00:00
|
|
|
you can add it in the `OAuth2Strategy` constructor:
|
2017-06-03 21:05:13 +00:00
|
|
|
|
|
|
|
```dart
|
|
|
|
configureServer(Angel app) async {
|
2019-01-06 00:43:06 +00:00
|
|
|
OAuth2Strategy(..., delimiter: ' ');
|
2017-06-03 21:05:13 +00:00
|
|
|
}
|
2018-03-30 16:44:15 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
## 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
|
2019-01-06 00:43:06 +00:00
|
|
|
OAuth2Strategy(
|
2018-03-30 16:44:15 +00:00
|
|
|
// ...
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-01-06 00:43:06 +00:00
|
|
|
throw FormatException('Invalid content-type $contentType; expected application/x-www-form-urlencoded or application/json.');
|
2018-03-30 16:44:15 +00:00
|
|
|
}
|
|
|
|
);
|
2017-06-03 21:05:13 +00:00
|
|
|
```
|