oauth2

Pubbuild status

A class containing handlers that can be used withinAngel to build a spec-compliantOAuth 2.0 server, including PKCE support.

Installation

In your pubspec.yaml:

1. dependencies:
2. angel_framework: ^2.0.0-alpha
3. angel_oauth2: ^2.0.0

Usage

Your server needs to have definitions of at least two types:

  • One model that represents a third-party application (client) trying to access a user's profile.
  • One that represents a user logged into the application.

Define a server class as such:

1. import 'package:angel_oauth2/angel_oauth2.dart' as oauth2;

3. class MyServer extends oauth2.AuthorizationServer<Client, User> {}

Then, implement the findClient and verifyClient to ensure that theserver class can not only identify a client application via a client_id,but that it can also verify its identity via a client_secret.

1. class _Server extends AuthorizationServer<PseudoApplication, Map> {
2. final Uuid _uuid = Uuid();

4. @override
5. FutureOr<PseudoApplication> findClient(String clientId) {
6. return clientId == pseudoApplication.id ? pseudoApplication : null;
7. }

9. @override
10. Future<bool> verifyClient(
11. PseudoApplication client, String clientSecret) async {
12. return client.secret == clientSecret;
13. }
14. }

Next, write some logic to be executed whenever a user visits theauthorization endpoint. In many cases, you will want to show a dialog:

1. @overrideFuture requestAuthorizationCode(  PseudoApplication client,  String redirectUri,  Iterable<String> scopes,  String state,  RequestContext req,  ResponseContext res) async {  res.render('dialog');}

Now, write logic that exchanges an authorization code for an access token,and optionally, a refresh token.

1. @overrideFuture<AuthorizationCodeResponse> exchangeAuthCodeForAccessToken(  String authCode,  String redirectUri,  RequestContext req,  ResponseContext res) async {    return AuthorizationCodeResponse('foo', refreshToken: 'bar');}

Now, set up some routes to point the server.

1. void pseudoCode() {
2. app.group('/oauth2', (router) {
3. router
4. ..get('/authorize', server.authorizationEndpoint)
5. ..post('/token', server.tokenEndpoint);
6. });
7. }

The authorizationEndpoint and tokenEndpoint handle all OAuth2 grant types.

Other Grants

By default, all OAuth2 grant methods will throw a 405 Method Not Allowed error.To support any specific grant type, all you need to do is implement the method.The following are available, not including authorization code grant support (mentioned above):

  • implicitGrant
  • resourceOwnerPasswordCredentialsGrant
  • clientCredentialsGrant
  • deviceCodeGrant

Read the OAuth2 specificationfor in-depth information on each grant type.

PKCE

In some cases, you will be using OAuth2 on a mobile device, or on some otherpublic client, where the client cannot have a clientsecret.

In such a case, you may consider usingPKCE.

Both the authorizationEndpoint and tokenEndpointinject a Pkce factory into the request, so itcan be used as follows:

1. @overrideFuture requestAuthorizationCode(    PseudoApplication client,    String redirectUri,    Iterable<String> scopes,    String state,    RequestContext req,    ResponseContext res) async {  // Automatically throws an error if the request doesn't contain the  // necessary information.  var pkce = req.container.make<Pkce>();
2. // At this point, store pkce.codeChallenge and pkce.codeChallengeMethod,  // so that when it's time to exchange the auth code for a token, we can  // create a [Pkce] object, and verify the client.  return await getAuthCodeSomehow(client, pkce.codeChallenge, pkce.codeChallengeMethod);}
3. @overrideFuture<AuthorizationTokenResponse> exchangeAuthorizationCodeForToken(    String authCode,    String redirectUri,    RequestContext req,    ResponseContext res) async {  // When exchanging the authorization code for a token, we'll need  // a code_verifier from the client, so that we can ensure  // that the correct client is trying to use the auth code.  //  // If none is present, an OAuth2 exception is thrown.  var codeVerifier = await getPkceCodeVerifier(req);
4. // Next, we'll need to retrieve the code challenge and code challenge method  // from earlier.  var codeChallenge = await getTheChallenge();  var codeChallengeMethod = await getTheChallengeMethod();
5. // Make a [Pkce] object.  var pkce = Pkce(codeChallengeMethod, codeChallenge);
6. // Call validate. If the client is invalid, it throws an OAuth2 exception.  pkce.validate(codeVerifier);
7. // If we reach here, we know that the code_verifier was valid,  // so we can return our authorization token as per usual.  return AuthorizationTokenResponse('…');}