diff --git a/src/main/javascript/view/AuthView.js b/src/main/javascript/view/AuthView.js index 5d692b84..8511a994 100644 --- a/src/main/javascript/view/AuthView.js +++ b/src/main/javascript/view/AuthView.js @@ -119,7 +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; + container.tokenName = dets.tokenName || 'access_token'; + this.passwordFlow(scopes, dets, container.OAuthSchemeKey); return; } else if(auth.get('grantTypes')) { @@ -156,17 +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, + 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 + }); + }, + + accessTokenRequest: function (scopes, oauth, OAuthSchemeKey, grantType, params) { + params = $.extend({}, { 'scope': scopes.join(' '), - 'grant_type': 'client_credentials' - }; + '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); @@ -177,5 +206,4 @@ SwaggerUi.Views.AuthView = Backbone.View.extend({ } }); } - }); diff --git a/src/main/javascript/view/Oauth2Model.js b/src/main/javascript/view/Oauth2Model.js index a31b9528..7f9d7d29 100644 --- a/src/main/javascript/view/Oauth2Model.js +++ b/src/main/javascript/view/Oauth2Model.js @@ -2,7 +2,9 @@ SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({ defaults: { - scopes: {} + scopes: {}, + isPasswordFlow: false, + clientAuthenticationType: 'none' }, initialize: function () { @@ -19,6 +21,13 @@ SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({ attributes.scopes = scopes; this.attributes = attributes; } + + 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); }, @@ -35,6 +44,16 @@ SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({ validate: function () { var valid = false; + if (this.get('isPasswordFlow') && + (!this.get('username'))) { + 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 6251511a..63d56f23 100644 --- a/src/main/javascript/view/Oauth2View.js +++ b/src/main/javascript/view/Oauth2View.js @@ -2,11 +2,20 @@ SwaggerUi.Views.Oauth2View = Backbone.View.extend({ events: { - 'change .oauth-scope': 'scopeChange' + 'change .oauth-scope': 'scopeChange', + 'change .oauth-username': 'setUsername', + 'change .oauth-password': 'setPassword', + 'change .oauth-client-authentication-type': 'setClientAuthenticationType', + 'change .oauth-client-id': 'setClientId', + 'change .oauth-client-secret': 'setClientSecret' }, template: Handlebars.templates.oauth2, + cls: { + error: 'error' + }, + render: function () { this.$el.html(this.template(this.model.toJSON())); @@ -18,5 +27,57 @@ SwaggerUi.Views.Oauth2View = Backbone.View.extend({ var scope = $(e.target).data('scope'); this.model.setScopes(scope, val); + }, + + setUsername: function (e) { + var val= $(e.target).val(); + this.model.set('username', val); + if (val) { + $(e.target).removeClass(this.cls.error); + } + }, + + setPassword: function (e) { + 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/less/auth.less b/src/main/less/auth.less index 67c18faa..c5d4338b 100644 --- a/src/main/less/auth.less +++ b/src/main/less/auth.less @@ -201,4 +201,9 @@ } .api-popup-actions { padding-top: 10px; } + + fieldset { + padding-bottom: 10px; + padding-left: 20px; + } } diff --git a/src/main/template/oauth2.handlebars b/src/main/template/oauth2.handlebars index 44cf5111..3bd2a361 100644 --- a/src/main/template/oauth2.handlebars +++ b/src/main/template/oauth2.handlebars @@ -1,12 +1,36 @@
-

Select OAuth2.0 Scopes

+

OAuth2.0

{{{sanitize description}}}

+ {{#if authorizationUrl}}

Authorization URL: {{{sanitize authorizationUrl}}}

{{/if}} + {{#if tokenUrl}}

Token URL: {{{sanitize tokenUrl}}}

{{/if}} +

flow: {{{escape flow}}}

+ {{#if isPasswordFlow}} +

Please input username and password for password flow authorization

+
+
+
+
+ {{/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

-

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

-

Authorization URL: {{{sanitize authorizationUrl}}}

-

flow: {{{escape flow}}}