From 63b4d40f508cc7c80e39004c5d2636931a3163a0 Mon Sep 17 00:00:00 2001 From: Blake Embrey Date: Thu, 13 Nov 2014 18:08:16 -0600 Subject: [PATCH] Enable addition OAuth 2.0 grant flows --- .../oauth2/credentials_manager.js | 34 ++++++++++++++ .../client/auth_strategies/oauth2/grant.js | 45 ++++++++++++++----- .../oauth2/request_access_token.js | 37 ++++++++++++--- app/scripts/directives/oauth2.js | 26 +++++++++++ app/views/oauth2.tmpl.html | 34 ++++++++++++-- app/views/security_schemes.tmpl.html | 6 +-- .../unit/client/auth_strategies/grant_spec.js | 4 ++ 7 files changed, 160 insertions(+), 26 deletions(-) diff --git a/app/scripts/client/auth_strategies/oauth2/credentials_manager.js b/app/scripts/client/auth_strategies/oauth2/credentials_manager.js index 1bfdc0fa2..3801ab5e9 100644 --- a/app/scripts/client/auth_strategies/oauth2/credentials_manager.js +++ b/app/scripts/client/auth_strategies/oauth2/credentials_manager.js @@ -19,6 +19,40 @@ grant_type: 'authorization_code', redirect_uri: RAML.Settings.oauth2RedirectUri }; + }, + + clientCredentialsParameters: function () { + return { + client_id: credentials.clientId, + client_secret: credentials.clientSecret, + grant_type: 'client_credentials' + }; + }, + + resourceOwnerParameters: function () { + var params = { + username: credentials.username, + password: credentials.password, + grant_type: 'password' + }; + + if (!credentials.clientSecret) { + params.client_id = credentials.clientId; + } + + return params; + }, + + resourceOwnerHeaders: function () { + if (!credentials.clientSecret) { + return {}; + } + + var authorization = btoa(credentials.clientId + ':' + credentials.clientSecret); + + return { + 'Authorization': 'Bearer' + authorization + }; } }; }; diff --git a/app/scripts/client/auth_strategies/oauth2/grant.js b/app/scripts/client/auth_strategies/oauth2/grant.js index 927eba1a3..06ce37b7d 100644 --- a/app/scripts/client/auth_strategies/oauth2/grant.js +++ b/app/scripts/client/auth_strategies/oauth2/grant.js @@ -1,27 +1,26 @@ (function() { 'use strict'; - var GRANTS = [ 'code', 'token' ], IMPLICIT_GRANT = 'token'; var Oauth2 = RAML.Client.AuthStrategies.Oauth2; - function grantTypeFrom(settings) { - var authorizationGrants = settings.authorizationGrants || []; - var filtered = authorizationGrants.filter(function(grant) { return grant === IMPLICIT_GRANT; }); - var specifiedGrant = filtered[0] || authorizationGrants[0]; - - if (!GRANTS.some(function(grant) { return grant === specifiedGrant; })) { - throw new Error('Unknown grant type: ' + specifiedGrant); - } - - return specifiedGrant; + var grants = { + code: true, + token: true, + owner: true, + credentials: true } var Grant = { create: function(settings, credentials) { - var type = grantTypeFrom(settings); + var type = credentials.grantType.type; var credentialsManager = Oauth2.credentialsManager(credentials, type); + if (!grants[type]) { + throw new Error('Unknown grant type: ' + type); + } + var className = type.charAt(0).toUpperCase() + type.slice(1); + return new this[className](settings, credentialsManager); } }; @@ -47,5 +46,27 @@ return Oauth2.requestAuthorization(this.settings, this.credentialsManager); }; + Grant.Owner = function(settings, credentialsManager) { + this.settings = settings; + this.credentialsManager = credentialsManager; + }; + + Grant.Owner.prototype.request = function() { + var requestToken = Oauth2.requestOwnerToken(this.settings, this.credentialsManager); + + return requestToken(); + }; + + Grant.Credentials = function(settings, credentialsManager) { + this.settings = settings; + this.credentialsManager = credentialsManager; + }; + + Grant.Credentials.prototype.request = function() { + var requestToken = Oauth2.requestCredentialsToken(this.settings, this.credentialsManager); + + return requestToken(); + }; + Oauth2.Grant = Grant; })(); diff --git a/app/scripts/client/auth_strategies/oauth2/request_access_token.js b/app/scripts/client/auth_strategies/oauth2/request_access_token.js index 46deab3f4..c987f56d0 100644 --- a/app/scripts/client/auth_strategies/oauth2/request_access_token.js +++ b/app/scripts/client/auth_strategies/oauth2/request_access_token.js @@ -18,21 +18,44 @@ return undefined; } + function extract(data) { + var method = accessTokenFromString; + + if (typeof data === 'object') { + method = accessTokenFromObject; + } + + return method(data); + } + RAML.Client.AuthStrategies.Oauth2.requestAccessToken = function(settings, credentialsManager) { return function(code) { var request = RAML.Client.Request.create(settings.accessTokenUri, 'post'); request.data(credentialsManager.accessTokenParameters(code)); - return $.ajax(request.toOptions()).then(function(data) { - var extract = accessTokenFromString; + return $.ajax(request.toOptions()).then(extract); + }; + }; + + RAML.Client.AuthStrategies.Oauth2.requestCredentialsToken = function(settings, credentialsManager) { + return function() { + var request = RAML.Client.Request.create(settings.accessTokenUri, 'post'); + + request.data(credentialsManager.clientCredentialsParameters()); + + return $.ajax(request.toOptions()).then(extract); + }; + }; + + RAML.Client.AuthStrategies.Oauth2.requestOwnerToken = function(settings, credentialsManager) { + return function() { + var request = RAML.Client.Request.create(settings.accessTokenUri, 'post'); - if (typeof data === 'object') { - extract = accessTokenFromObject; - } + request.headers(credentialsManager.resourceOwnerHeaders()); + request.data(credentialsManager.resourceOwnerParameters()); - return extract(data); - }); + return $.ajax(request.toOptions()).then(extract); }; }; })(); diff --git a/app/scripts/directives/oauth2.js b/app/scripts/directives/oauth2.js index 400cea401..c5a03a4da 100644 --- a/app/scripts/directives/oauth2.js +++ b/app/scripts/directives/oauth2.js @@ -2,11 +2,37 @@ (function() { RAML.Directives.oauth2 = function() { + + var GRANT_TYPES = [ + { name: 'Implicit', type: 'token' }, + { name: 'Authorization Code', type: 'code' }, + { name: 'Client Credentials', type: 'credentials' }, + { name: 'Resource Owner Password Credentials', type: 'owner' } + ]; + + var controller = function($scope) { + var authorizationGrants = $scope.scheme.settings.authorizationGrants; + + $scope.grantTypes = GRANT_TYPES.filter(function (grant) { + return authorizationGrants.indexOf(grant.type) > -1; + }); + + $scope.credentials = { + clientId: '', + clientSecret: '', + username: '', + password: '', + grantType: $scope.grantTypes[0] + }; + }; + return { restrict: 'E', templateUrl: 'views/oauth2.tmpl.html', replace: true, + controller: controller, scope: { + scheme: '=', credentials: '=' } }; diff --git a/app/views/oauth2.tmpl.html b/app/views/oauth2.tmpl.html index b01d7de38..dca6ac30c 100644 --- a/app/views/oauth2.tmpl.html +++ b/app/views/oauth2.tmpl.html @@ -1,11 +1,37 @@
- - + +
- - + + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + +
diff --git a/app/views/security_schemes.tmpl.html b/app/views/security_schemes.tmpl.html index 7e57197d0..d0ec0e0af 100644 --- a/app/views/security_schemes.tmpl.html +++ b/app/views/security_schemes.tmpl.html @@ -6,9 +6,9 @@

Authentication

- - - + + +
diff --git a/spec/unit/client/auth_strategies/grant_spec.js b/spec/unit/client/auth_strategies/grant_spec.js index b8275bbc1..e04e2336b 100644 --- a/spec/unit/client/auth_strategies/grant_spec.js +++ b/spec/unit/client/auth_strategies/grant_spec.js @@ -18,6 +18,7 @@ describe("RAML.Client.AuthStrategies.Oauth2.Grant", function() { beforeEach(function() { tokenSpy = spyOn(RAML.Client.AuthStrategies.Oauth2.Grant, 'Token'); settings = { authorizationGrants: ['token'] }; + credentials.grantType = { type: 'token' }; RAML.Client.AuthStrategies.Oauth2.Grant.create(settings, credentials); }); @@ -36,6 +37,7 @@ describe("RAML.Client.AuthStrategies.Oauth2.Grant", function() { beforeEach(function() { codeSpy = spyOn(RAML.Client.AuthStrategies.Oauth2.Grant, 'Code'); settings = { authorizationGrants: ['code'] }; + credentials.grantType = { type: 'code' }; RAML.Client.AuthStrategies.Oauth2.Grant.create(settings, credentials); }); @@ -51,6 +53,7 @@ describe("RAML.Client.AuthStrategies.Oauth2.Grant", function() { describe("with an unknown authorizationGrant", function() { beforeEach(function() { settings = { authorizationGrants: ['unknown'] }; + credentials.grantType = { type: 'unknown' }; }); it("throws", function() { @@ -63,6 +66,7 @@ describe("RAML.Client.AuthStrategies.Oauth2.Grant", function() { describe("when no authorizationGrant is present", function() { beforeEach(function() { settings = {}; + credentials.grantType = {}; }); it("throws", function() {