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 89178696..8511a994 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 c6058e8b..7f9d7d29 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 3bdd97fd..63d56f23 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 35db3a61..3bd2a361 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