From 3494d44d3f40eb8b451c191f991a1c4dff938b19 Mon Sep 17 00:00:00 2001 From: TANAKA Koichi Date: Sat, 12 Nov 2016 23:17:10 +0900 Subject: [PATCH] Implement OAuth2 client authentication for password and application flow --- src/main/javascript/view/AuthView.js | 59 +++++++++++++------------ src/main/javascript/view/Oauth2Model.js | 15 ++++++- src/main/javascript/view/Oauth2View.js | 39 +++++++++++++++- src/main/template/oauth2.handlebars | 16 +++++++ 4 files changed, 97 insertions(+), 32 deletions(-) diff --git a/src/main/javascript/view/AuthView.js b/src/main/javascript/view/AuthView.js index 891786963de..8511a994f81 100644 --- a/src/main/javascript/view/AuthView.js +++ b/src/main/javascript/view/AuthView.js @@ -119,13 +119,13 @@ SwaggerUi.Views.AuthView = Backbone.View.extend({ else if(auth.get('type') === 'oauth2' && flow && (flow === 'application')) { dets = auth.attributes; container.tokenName = dets.tokenName || 'access_token'; - this.clientCredentialsFlow(scopes, dets.tokenUrl, container.OAuthSchemeKey); + this.clientCredentialsFlow(scopes, dets, container.OAuthSchemeKey); return; } else if(auth.get('type') === 'oauth2' && flow && (flow === 'password')) { dets = auth.attributes; - window.swaggerUi.tokenName = dets.tokenName || 'access_token'; - this.passwordFlow(scopes, dets.tokenUrl, dets.username, dets.password, window.OAuthSchemeKey); + container.tokenName = dets.tokenName || 'access_token'; + this.passwordFlow(scopes, dets, container.OAuthSchemeKey); return; } else if(auth.get('grantTypes')) { @@ -162,39 +162,40 @@ SwaggerUi.Views.AuthView = Backbone.View.extend({ }, // taken from lib/swagger-oauth.js - clientCredentialsFlow: function (scopes, tokenUrl, OAuthSchemeKey) { - var params = { - 'client_id': clientId, - 'client_secret': clientSecret, - 'scope': scopes.join(' '), - 'grant_type': 'client_credentials' - }; - $.ajax({ - url : tokenUrl, - type: 'POST', - data: params, - success: function (data) - { - onOAuthComplete(data, OAuthSchemeKey); - }, - error: function () - { - onOAuthComplete(''); - } + clientCredentialsFlow: function (scopes, oauth, OAuthSchemeKey) { + this.accessTokenRequest(scopes, oauth, OAuthSchemeKey, 'client_credentials'); + }, + + passwordFlow: function (scopes, oauth, OAuthSchemeKey) { + this.accessTokenRequest(scopes, oauth, OAuthSchemeKey, 'password', { + 'username': oauth.username, + 'password': oauth.password }); }, - passwordFlow: function (scopes, tokenUrl, username, password, OAuthSchemeKey) { - var params = { + accessTokenRequest: function (scopes, oauth, OAuthSchemeKey, grantType, params) { + params = $.extend({}, { 'scope': scopes.join(' '), - 'username': username, - 'password': password, - 'grant_type': 'password' - }; + 'grant_type': grantType + }, params); + + var headers= {}; + + switch (oauth.clientAuthenticationType) { + case 'basic': + headers.Authorization = 'Basic ' + btoa(oauth.clientId + ':' + oauth.clientSecret); + break; + case 'request-body': + params.client_id = oauth.clientId; + params.client_secret = oauth.clientSecret; + break; + } + $.ajax({ - url : tokenUrl, + url : oauth.tokenUrl, type: 'POST', data: params, + headers: headers, success: function (data) { onOAuthComplete(data, OAuthSchemeKey); diff --git a/src/main/javascript/view/Oauth2Model.js b/src/main/javascript/view/Oauth2Model.js index c6058e8bf6d..7f9d7d2979d 100644 --- a/src/main/javascript/view/Oauth2Model.js +++ b/src/main/javascript/view/Oauth2Model.js @@ -3,7 +3,8 @@ SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({ defaults: { scopes: {}, - isPasswordFlow: false + isPasswordFlow: false, + clientAuthenticationType: 'none' }, initialize: function () { @@ -21,7 +22,12 @@ SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({ this.attributes = attributes; } - this.set('isPasswordFlow', attributes.flow && attributes.flow === 'password'); + if (this.attributes && this.attributes.flow) { + var flow = this.attributes.flow; + this.set('isPasswordFlow', flow === 'password'); + this.set('requireClientAuthentication', flow === 'application'); + this.set('clientAuthentication', flow === 'password' || flow === 'application'); + } this.on('change', this.validate); }, @@ -43,6 +49,11 @@ SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({ return false; } + if (this.get('clientAuthenticationType') in ['basic', 'request-body'] && + (!this.get('clientId'))) { + return false; + } + var scp = this.get('scopes'); var idx = _.findIndex(scp, function (o) { return o.checked === true; diff --git a/src/main/javascript/view/Oauth2View.js b/src/main/javascript/view/Oauth2View.js index 3bdd97fd8ea..63d56f23b6a 100644 --- a/src/main/javascript/view/Oauth2View.js +++ b/src/main/javascript/view/Oauth2View.js @@ -4,7 +4,10 @@ SwaggerUi.Views.Oauth2View = Backbone.View.extend({ events: { 'change .oauth-scope': 'scopeChange', 'change .oauth-username': 'setUsername', - 'change .oauth-password': 'setPassword' + 'change .oauth-password': 'setPassword', + 'change .oauth-client-authentication-type': 'setClientAuthenticationType', + 'change .oauth-client-id': 'setClientId', + 'change .oauth-client-secret': 'setClientSecret' }, template: Handlebars.templates.oauth2, @@ -38,9 +41,43 @@ SwaggerUi.Views.Oauth2View = Backbone.View.extend({ this.model.set('password', $(e.target).val()); }, + setClientAuthenticationType: function (e) { + var type = $(e.target).val(); + var $el = this.$el; + this.model.set('clientAuthenticationType', type); + + switch(type) { + case 'none': + $el.find('.oauth-client-authentication').hide(); + break; + case 'basic': + case 'request-body': + $el.find('.oauth-client-id').removeClass(this.cls.error); + $el.find('.oauth-client-authentication').show(); + break; + } + }, + + setClientId: function (e) { + var val = $(e.target).val(); + this.model.set('clientId', val); + if (val) { + $(e.target).removeClass(this.cls.error); + } + }, + + setClientSecret: function (e) { + this.model.set('clientSecret', $(e.target).val()); + $(e.target).removeClass('error'); + }, + highlightInvalid: function () { if (!this.model.get('username')) { this.$el.find('.oauth-username').addClass(this.cls.error); } + + if (!this.model.get('clientId')) { + this.$el.find('.oauth-client-id').addClass(this.cls.error); + } } }); \ No newline at end of file diff --git a/src/main/template/oauth2.handlebars b/src/main/template/oauth2.handlebars index 35db3a61352..3bd2a3612d1 100644 --- a/src/main/template/oauth2.handlebars +++ b/src/main/template/oauth2.handlebars @@ -11,6 +11,22 @@
{{/if}} + {{#if clientAuthentication}} +

Setup client authentication.{{#if requireClientAuthenticaiton}}(Required){{/if}}

+
+
+ +
+ {{/if}}

{{{escape appName}}} API requires the following scopes. Select which ones you want to grant to Swagger UI.

Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes. Learn how to use