From 2afe4deab0256f334cab0840a0308fc008b220c4 Mon Sep 17 00:00:00 2001 From: Tobe O Date: Fri, 3 May 2019 03:24:24 -0400 Subject: [PATCH] implicit --- CHANGELOG.md | 3 + example/main.dart | 3 +- lib/src/server.dart | 126 +++++++++++----------------------- pubspec.yaml | 2 +- test/auth_code_test.dart | 9 ++- test/implicit_grant_test.dart | 9 ++- test/pkce_test.dart | 3 +- 7 files changed, 62 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fd6d44f..5c550e2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +# 2.3.0 +* Remove `implicitGrant`, and inline it into `requestAuthorizationCode`. + # 2.2.0+1 * Parse+verify client for `authorization_code`. diff --git a/example/main.dart b/example/main.dart index 58527be7..0da19454 100644 --- a/example/main.dart +++ b/example/main.dart @@ -56,7 +56,8 @@ class _ExampleAuthorizationServer Iterable scopes, String state, RequestContext req, - ResponseContext res) { + ResponseContext res, + bool implicit) { // TODO: In many cases, here you will render a view displaying to the user which scopes are being requested. throw UnimplementedError(); } diff --git a/lib/src/server.dart b/lib/src/server.dart index 457cc319..3a103ec7 100644 --- a/lib/src/server.dart +++ b/lib/src/server.dart @@ -89,34 +89,18 @@ abstract class AuthorizationServer { /// Prompt the currently logged-in user to grant or deny access to the [client]. /// /// In many applications, this will entail showing a dialog to the user in question. - FutureOr requestAuthorizationCode( - Client client, - String redirectUri, - Iterable scopes, - String state, - RequestContext req, - ResponseContext res) { - throw AuthorizationException( - ErrorResponse( - ErrorResponse.unsupportedResponseType, - 'Authorization code grants are not supported.', - state, - ), - statusCode: 400, - ); - } - - /// Create an implicit authorization token. /// - /// Note that in cases where this is called, there is no guarantee - /// that the user agent has not been compromised. - FutureOr implicitGrant( + /// If [implicit] is `true`, then the client is requesting an *implicit grant*. + /// Be aware of the security implications of this - do not handle them exactly + /// the same. + FutureOr requestAuthorizationCode( Client client, String redirectUri, Iterable scopes, String state, RequestContext req, - ResponseContext res) { + ResponseContext res, + bool implicit) { throw AuthorizationException( ErrorResponse( ErrorResponse.unsupportedResponseType, @@ -227,9 +211,39 @@ abstract class AuthorizationServer { ); } + /// Returns the [Uri] that a client can be redirected to in the case of an implicit grant. + Uri completeImplicitGrant(AuthorizationTokenResponse token, Uri redirectUri, + {String state}) { + var queryParameters = {}; + + queryParameters.addAll({ + 'access_token': token.accessToken, + 'token_type': 'bearer', + }); + + if (state != null) queryParameters['state'] = state; + + if (token.expiresIn != null) + queryParameters['expires_in'] = token.expiresIn.toString(); + + if (token.scope != null) queryParameters['scope'] = token.scope.join(' '); + + var fragment = + queryParameters.keys.fold(StringBuffer(), (buf, k) { + if (buf.isNotEmpty) buf.write('&'); + return buf + ..write( + '$k=' + Uri.encodeComponent(queryParameters[k]), + ); + }).toString(); + + return redirectUri.replace(fragment: fragment); + } + /// A request handler that invokes the correct logic, depending on which type /// of grant the client is requesting. - Future authorizationEndpoint(RequestContext req, ResponseContext res) async { + Future authorizationEndpoint( + RequestContext req, ResponseContext res) async { String state = ''; try { @@ -241,7 +255,7 @@ abstract class AuthorizationServer { return Pkce.fromJson(req.queryParameters, state: state); }); - if (responseType == 'code') { + if (responseType == 'code' || responseType == 'token') { // Ensure client ID var clientId = await _getParam(req, 'client_id', state); @@ -262,68 +276,8 @@ abstract class AuthorizationServer { // Grab scopes var scopes = await _getScopes(req); - return await requestAuthorizationCode( - client, redirectUri, scopes, state, req, res); - } - - if (responseType == 'token') { - var clientId = await _getParam(req, 'client_id', state); - var client = await findClient(clientId); - - if (client == null) { - throw AuthorizationException(ErrorResponse( - ErrorResponse.unauthorizedClient, - 'Unknown client "$clientId".', - state, - )); - } - - var redirectUri = await _getParam(req, 'redirect_uri', state); - - // Grab scopes - var scopes = await _getScopes(req); - var token = - await implicitGrant(client, redirectUri, scopes, state, req, res); - - Uri target; - - try { - target = Uri.parse(redirectUri); - var queryParameters = {}; - - queryParameters.addAll({ - 'access_token': token.accessToken, - 'token_type': 'bearer', - 'state': state, - }); - - if (token.expiresIn != null) - queryParameters['expires_in'] = token.expiresIn.toString(); - - if (token.scope != null) - queryParameters['scope'] = token.scope.join(' '); - - var fragment = - queryParameters.keys.fold(StringBuffer(), (buf, k) { - if (buf.isNotEmpty) buf.write('&'); - return buf - ..write( - '$k=' + Uri.encodeComponent(queryParameters[k]), - ); - }).toString(); - - target = target.replace(fragment: fragment); - await res.redirect(target.toString()); - return false; - } on FormatException { - throw AuthorizationException( - ErrorResponse( - ErrorResponse.invalidRequest, - 'Invalid URI provided as "redirect_uri" parameter', - state, - ), - statusCode: 400); - } + return await requestAuthorizationCode(client, redirectUri, scopes, + state, req, res, responseType == 'token'); } throw AuthorizationException( diff --git a/pubspec.yaml b/pubspec.yaml index 2c33082e..395ad3fc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: angel_oauth2 author: Tobe O description: A class containing handlers that can be used within Angel to build a spec-compliant OAuth 2.0 server. homepage: https://github.com/angel-dart/oauth2.git -version: 2.2.0+1 +version: 2.3.0 environment: sdk: ">=2.0.0-dev <3.0.0" dependencies: diff --git a/test/auth_code_test.dart b/test/auth_code_test.dart index ff714e1f..043305c3 100644 --- a/test/auth_code_test.dart +++ b/test/auth_code_test.dart @@ -139,7 +139,14 @@ class _Server extends AuthorizationServer { Iterable scopes, String state, RequestContext req, - ResponseContext res) async { + ResponseContext res, + bool implicit) async { + if (implicit) { + // Throw the default error on an implicit grant attempt. + return super.requestAuthorizationCode( + client, redirectUri, scopes, state, req, res, implicit); + } + if (state == 'hello') return 'Hello ${pseudoApplication.id}:${pseudoApplication.secret}'; diff --git a/test/implicit_grant_test.dart b/test/implicit_grant_test.dart index 49ae44b4..c1dba844 100644 --- a/test/implicit_grant_test.dart +++ b/test/implicit_grant_test.dart @@ -58,13 +58,16 @@ class _AuthorizationServer } @override - Future implicitGrant( + Future requestAuthorizationCode( PseudoApplication client, String redirectUri, Iterable scopes, String state, RequestContext req, - ResponseContext res) async { - return AuthorizationTokenResponse('foo'); + ResponseContext res, + bool implicit) async { + var tok = AuthorizationTokenResponse('foo'); + var uri = completeImplicitGrant(tok, Uri.parse(redirectUri), state: state); + return res.redirect(uri); } } diff --git a/test/pkce_test.dart b/test/pkce_test.dart index ae806ed3..d8d901bc 100644 --- a/test/pkce_test.dart +++ b/test/pkce_test.dart @@ -243,7 +243,8 @@ class _Server extends AuthorizationServer { Iterable scopes, String state, RequestContext req, - ResponseContext res) async { + ResponseContext res, + bool implicit) async { req.container.make(); return {'code': 'ok'}; }