Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added basic support for oauth2-password-flow #1574

Closed
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 97 additions & 43 deletions lib/swagger-oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,33 @@ var scopeSeparator;
function handleLogin() {
var scopes = [];

var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions;
var auths = window.swaggerUi.api.authSchemes || window.swaggerUi.api.securityDefinitions,
passwordFlow = false;

if(auths) {
var key;
var defs = auths;
for(key in defs) {
var auth = defs[key];
if(auth.type === 'oauth2' && auth.scopes) {
oauth2KeyName = key;
var scope;
if(Array.isArray(auth.scopes)) {
// 1.2 support
var i;
for(i = 0; i < auth.scopes.length; i++) {
scopes.push(auth.scopes[i]);
if(auth.type === 'oauth2') {
passwordFlow = auth.flow === 'password';

if (auth.scopes) {
oauth2KeyName = key;
var scope;

if(Array.isArray(auth.scopes)) {
// 1.2 support
var i;
for(i = 0; i < auth.scopes.length; i++) {
scopes.push(auth.scopes[i]);
}
}
}
else {
// 2.0 support
for(scope in auth.scopes) {
scopes.push({scope: scope, description: auth.scopes[scope]});
else {
// 2.0 support
for(scope in auth.scopes) {
scopes.push({scope: scope, description: auth.scopes[scope]});
}
}
}
}
Expand All @@ -42,22 +49,32 @@ function handleLogin() {
appName = window.swaggerUi.api.info.title;
}

$('.api-popup-dialog').remove();
popupDialog = $(
[
'<div class="api-popup-dialog">',
'<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
'<div class="api-popup-content">',
'<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>',
'</p>',
'<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
'<ul class="api-popup-scopes">',
'</ul>',
'<p class="error-msg"></p>',
'<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
'</div>',
'</div>'].join(''));
$('.api-popup-dialog').remove();

popupDialog = ['<div class="api-popup-dialog">'];

if (passwordFlow === true) {
popupDialog = popupDialog.concat([
'<div class="api-popup-title">Password Auth</div>',
'<p><label>Username: </label> <input type="text" id="username"> ',
'<label>Password: </label> <input type="password" id="password"></p>'
]);
}

popupDialog = $(popupDialog.concat([
'<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
'<div class="api-popup-content">',
'<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>',
'</p>',
'<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
'<ul class="api-popup-scopes">',
'</ul>',
'<p class="error-msg"></p>',
'<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
'</div>',
'</div>']).join(''));

$(document.body).append(popupDialog);

popup = popupDialog.find('ul.api-popup-scopes').empty();
Expand Down Expand Up @@ -98,21 +115,31 @@ function handleLogin() {
popupDialog.hide();

var authSchemes = window.swaggerUi.api.authSchemes;

var host = window.location;
var pathname = location.pathname.substring(0, location.pathname.lastIndexOf("/"));
var defaultRedirectUrl = host.protocol + '//' + host.host + pathname + '/o2c.html';
var redirectUrl = window.oAuthRedirectUrl || defaultRedirectUrl;
var url = null;
var passwordFlowDetails = null;

for (var key in authSchemes) {
if (authSchemes.hasOwnProperty(key)) {
var flow = authSchemes[key].flow;

if(authSchemes[key].type === 'oauth2' && flow && (flow === 'implicit' || flow === 'accessCode')) {
if (authSchemes[key].type === 'oauth2' && flow) {
var dets = authSchemes[key];
url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code');
window.swaggerUi.tokenName = dets.tokenName || 'access_token';
window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);

if (flow === 'password') {
passwordFlowDetails = dets;
window.swaggerUi.tokenUrl = dets.tokenUrl;

} else if (['implicit', 'accessCode'].indexOf(flow) !== -1) {

url = dets.authorizationUrl + '?response_type=' + (flow === 'implicit' ? 'token' : 'code');
window.swaggerUi.tokenUrl = (flow === 'accessCode' ? dets.tokenUrl : null);
}
}
else if(authSchemes[key].grantTypes) {
// 1.2 support
Expand Down Expand Up @@ -144,27 +171,54 @@ function handleLogin() {
scopes.push(scope);
}

// Implicit auth recommends a state parameter.
var state = Math.random ();
if (passwordFlowDetails !== null) {
handlePasswordFlow(passwordFlowDetails);

} else {
// Implicit auth recommends a state parameter.
var state = Math.random ();

window.enabledScopes=scopes;
window.enabledScopes=scopes;

redirect_uri = redirectUrl;
redirect_uri = redirectUrl;

url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes.join(scopeSeparator));
url += '&state=' + encodeURIComponent(state);
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes.join(scopeSeparator));
url += '&state=' + encodeURIComponent(state);

window.open(url);
window.open(url);
}
});

popupMask.show();
popupDialog.show();
return;
}

function handlePasswordFlow(auth) {
var tokenUri = auth.tokenUrl;

var authParams = {
grant_type: 'password',
client_id: encodeURIComponent(clientId),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may I suggest adding the client_secret here as well?
client_secret: encodeURIComponent(clientSecret),

at least it helped me using password flow with my REST API endpoint which requird a secret as well for token generation

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my last push I added the client secret to the auth headers for basic auth. I believe Spring Security requires basic auth for getting a token. Does it solve your problem as well @pkerspe or does it need to be a query param?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately cannot confirm that the basic auth header part would be working for me, was trying with your last push, but had to roll back. I am using the shaffer/oauth2-server-php by the way. Grant type is UserCredentials (password).

username: $('#username').val(),
password: encodeURIComponent($('#password').val()),
};

$.ajax({
url: auth.tokenUrl,
type: 'post',
data: authParams,
success: function (data) {
onOAuthComplete(data);
},
error: function(data) {
onOAuthComplete("");
}
});
}

function handleLogout() {
for(key in window.swaggerUi.api.clientAuthorizations.authz){
Expand Down