Merge pull request #2397 from MugeSo/implement-oauth2-password-flow

Implement OAuth2 password flow
This commit is contained in:
Ron
2016-12-06 08:52:16 -08:00
committed by GitHub
5 changed files with 152 additions and 15 deletions

View File

@@ -119,7 +119,13 @@ SwaggerUi.Views.AuthView = Backbone.View.extend({
else if(auth.get('type') === 'oauth2' && flow && (flow === 'application')) { else if(auth.get('type') === 'oauth2' && flow && (flow === 'application')) {
dets = auth.attributes; dets = auth.attributes;
container.tokenName = dets.tokenName || 'access_token'; 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; return;
} }
else if(auth.get('grantTypes')) { else if(auth.get('grantTypes')) {
@@ -156,17 +162,40 @@ SwaggerUi.Views.AuthView = Backbone.View.extend({
}, },
// taken from lib/swagger-oauth.js // taken from lib/swagger-oauth.js
clientCredentialsFlow: function (scopes, tokenUrl, OAuthSchemeKey) { clientCredentialsFlow: function (scopes, oauth, OAuthSchemeKey) {
var params = { this.accessTokenRequest(scopes, oauth, OAuthSchemeKey, 'client_credentials');
'client_id': clientId, },
'client_secret': clientSecret,
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(' '), '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({ $.ajax({
url : tokenUrl, url : oauth.tokenUrl,
type: 'POST', type: 'POST',
data: params, data: params,
headers: headers,
success: function (data) success: function (data)
{ {
onOAuthComplete(data, OAuthSchemeKey); onOAuthComplete(data, OAuthSchemeKey);
@@ -177,5 +206,4 @@ SwaggerUi.Views.AuthView = Backbone.View.extend({
} }
}); });
} }
}); });

View File

@@ -2,7 +2,9 @@
SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({ SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({
defaults: { defaults: {
scopes: {} scopes: {},
isPasswordFlow: false,
clientAuthenticationType: 'none'
}, },
initialize: function () { initialize: function () {
@@ -19,6 +21,13 @@ SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({
attributes.scopes = scopes; attributes.scopes = scopes;
this.attributes = attributes; 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); this.on('change', this.validate);
}, },
@@ -35,6 +44,16 @@ SwaggerUi.Models.Oauth2Model = Backbone.Model.extend({
validate: function () { validate: function () {
var valid = false; 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 scp = this.get('scopes');
var idx = _.findIndex(scp, function (o) { var idx = _.findIndex(scp, function (o) {
return o.checked === true; return o.checked === true;

View File

@@ -2,11 +2,20 @@
SwaggerUi.Views.Oauth2View = Backbone.View.extend({ SwaggerUi.Views.Oauth2View = Backbone.View.extend({
events: { 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, template: Handlebars.templates.oauth2,
cls: {
error: 'error'
},
render: function () { render: function () {
this.$el.html(this.template(this.model.toJSON())); this.$el.html(this.template(this.model.toJSON()));
@@ -18,5 +27,57 @@ SwaggerUi.Views.Oauth2View = Backbone.View.extend({
var scope = $(e.target).data('scope'); var scope = $(e.target).data('scope');
this.model.setScopes(scope, val); 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);
}
} }
}); });

View File

@@ -201,4 +201,9 @@
} }
.api-popup-actions { padding-top: 10px; } .api-popup-actions { padding-top: 10px; }
fieldset {
padding-bottom: 10px;
padding-left: 20px;
}
} }

View File

@@ -1,12 +1,36 @@
<div> <div>
<h3 class="auth__title">Select OAuth2.0 Scopes</h3> <h3 class="auth__title">OAuth2.0</h3>
<p>{{{sanitize description}}}</p> <p>{{{sanitize description}}}</p>
{{#if authorizationUrl}}<p>Authorization URL: {{{sanitize authorizationUrl}}}</p>{{/if}}
{{#if tokenUrl}}<p>Token URL: {{{sanitize tokenUrl}}}</p>{{/if}}
<p>flow: {{{escape flow}}}</p>
{{#if isPasswordFlow}}
<p>Please input username and password for password flow authorization</p>
<fieldset>
<div><label>Username: <input class="oauth-username" type="text" name="username"></label></div>
<div><label>Password: <input class="oauth-password" type="password" name="password"></label></div>
</fieldset>
{{/if}}
{{#if clientAuthentication}}
<p>Setup client authentication.{{#if requireClientAuthenticaiton}}(Required){{/if}}</p>
<fieldset>
<div><label>Type:
<select class="oauth-client-authentication-type" name="client-authentication-type">
<option value="none" selected>None or other</option>
<option value="basic">Basic auth</option>
<option value="request-body">Request body</option>
</select>
</label></div>
<div class="oauth-client-authentication" hidden>
<div><label>ClientId: <input class="oauth-client-id" type="text" name="client-id"></label></div>
<div><label>Secret: <input class="oauth-client-secret" type="text" name="client-secret"></label></div>
</div>
</fieldset>
{{/if}}
<p><strong> {{{escape appName}}} </strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>
<p>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. <p>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.
<a href="#">Learn how to use</a> <a href="#">Learn how to use</a>
</p> </p>
<p><strong> {{{escape appName}}} </strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>
<p>Authorization URL: {{{sanitize authorizationUrl}}}</p>
<p>flow: {{{escape flow}}}</p>
<ul class="api-popup-scopes"> <ul class="api-popup-scopes">
{{#each scopes}} {{#each scopes}}
<li> <li>