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

Add support for "grantType" as alias for PKCE #235

Merged
merged 2 commits into from
Aug 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions jest.server.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
'**/test/spec/*.js'
],
'testPathIgnorePatterns': [
'./test/spec/browser.js',
'./test/spec/fingerprint.js',
'./test/spec/general.js',
'./test/spec/oauthUtil.js',
Expand Down
4 changes: 3 additions & 1 deletion lib/browser/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,16 @@ function OktaAuthBuilder(args) {
var sdk = this;

var url = builderUtil.getValidUrl(args);
// OKTA-242989: support for grantType will be removed in 3.0
var usePKCE = args.pkce || args.grantType === 'authorization_code';

Choose a reason for hiding this comment

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

This change overrides my configuration when I have pkce: false and grantType: 'authorization_code'.

Do you 'have' to use pkce with an auth_code grant?

There are a few places in the code that do this type of boolean coalesce operation that override an explicit parameter that is already set to false

Copy link
Contributor Author

@aarongranick-okta aarongranick-okta Aug 23, 2019

Choose a reason for hiding this comment

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

The grantType parameter was added specifically for PKCE, it has no other function. We have deprecated it because of the confusion it has caused with non-PKCE ("standard") authorization_code flow. To perform "standard" authorization_code flow, you should set the responseType to ['code']. Do not use the grantType option, it is only an alias for pkce and we will remove it in the near future.

this.options = {
url: util.removeTrailingSlash(url),
clientId: args.clientId,
issuer: util.removeTrailingSlash(args.issuer),
authorizeUrl: util.removeTrailingSlash(args.authorizeUrl),
userinfoUrl: util.removeTrailingSlash(args.userinfoUrl),
tokenUrl: util.removeTrailingSlash(args.tokenUrl),
pkce: args.pkce,
pkce: usePKCE,
redirectUri: args.redirectUri,
httpRequestClient: args.httpRequestClient,
storageUtil: args.storageUtil,
Expand Down
32 changes: 19 additions & 13 deletions lib/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,8 @@ function handleOAuthResponse(sdk, oauthParams, res, urls) {
});
}

function getDefaultOAuthParams(sdk, oauthOptions) {
oauthOptions = util.clone(oauthOptions) || {};

var defaults = {
function getDefaultOAuthParams(sdk) {
return {
pkce: sdk.options.pkce || false,
clientId: sdk.options.clientId,
redirectUri: sdk.options.redirectUri || window.location.href,
Expand All @@ -258,14 +256,6 @@ function getDefaultOAuthParams(sdk, oauthOptions) {
scopes: ['openid', 'email'],
ignoreSignature: sdk.options.ignoreSignature
};
util.extend(defaults, oauthOptions);

// PKCE flow, set default code challenge method
if (defaults.pkce && !defaults.codeChallengeMethod) {
defaults.codeChallengeMethod = PKCE.DEFAULT_CODE_CHALLENGE_METHOD;
}

return defaults;
}

function convertOAuthParamsToQueryParams(oauthParams) {
Expand Down Expand Up @@ -519,7 +509,18 @@ function getWithPopup(sdk, oauthOptions, options) {
}

function prepareOauthParams(sdk, oauthOptions) {
var oauthParams = getDefaultOAuthParams(sdk, oauthOptions);
// clone and prepare options
oauthOptions = util.clone(oauthOptions) || {};

// OKTA-242989: support for grantType will be removed in 3.0
if (oauthOptions.grantType === 'authorization_code') {
oauthOptions.pkce = true;
}

// build params using defaults + options
var oauthParams = getDefaultOAuthParams(sdk);
util.extend(oauthParams, oauthOptions);

if (oauthParams.pkce !== true) {
return Q.resolve(oauthParams);
}
Expand All @@ -529,6 +530,11 @@ function prepareOauthParams(sdk, oauthOptions) {
return Q.reject(new AuthSdkError('This browser doesn\'t support PKCE'));
}

// set default code challenge method, if none provided
if (!oauthParams.codeChallengeMethod) {
oauthParams.codeChallengeMethod = PKCE.DEFAULT_CODE_CHALLENGE_METHOD;
}

// responseType is forced
oauthParams.responseType = 'code';

Expand Down
45 changes: 45 additions & 0 deletions test/spec/browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
var OktaAuth = require('../../lib/browser/browserIndex');


describe('Browser', function() {


it('is a valid constructor', function() {
var auth = new OktaAuth({ url: 'http://localhost/fake' });
expect(auth instanceof OktaAuth).toBe(true);
});

describe('options', function() {
var auth;
beforeEach(function() {
auth = new OktaAuth({ url: 'http://localhost/fake' });
});

describe('PKCE', function() {

it('is false by default', function() {
expect(auth.options.pkce).toBe(false);
});

it('can be set by arg', function() {
spyOn(OktaAuth.features, 'isPKCESupported').and.returnValue(true);
auth = new OktaAuth({ pkce: true, url: 'http://localhost/fake' });
expect(auth.options.pkce).toBe(true);
});

it('accepts alias "grantType"', function() {
spyOn(OktaAuth.features, 'isPKCESupported').and.returnValue(true);
auth = new OktaAuth({ grantType: "authorization_code", url: 'http://localhost/fake' });
expect(auth.options.pkce).toBe(true);
});

it('throws if PKCE is not supported', function() {
spyOn(OktaAuth.features, 'isPKCESupported').and.returnValue(false);
function fn() {
auth = new OktaAuth({ pkce: true, url: 'http://localhost/fake' });
}
expect(fn).toThrowError('This browser doesn\'t support PKCE');
});
})
});
});
51 changes: 51 additions & 0 deletions test/spec/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,57 @@ describe('token.getWithRedirect', function() {
});
});

it('PKCE: can use grantType="authorization_code" as an alias for pkce: true', function() {
mockPKCE();
return oauthUtil.setupRedirect({
oktaAuthArgs: {
grantType: "authorization_code", // alias for pkce: true
},
getWithRedirectArgs: {
sessionToken: 'testToken',
responseType: 'code'
},
expectedCookies: [
[
'okta-oauth-redirect-params',
JSON.stringify({
responseType: 'code',
state: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
nonce: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
scopes: ['openid', 'email'],
clientId: 'NPSfOkH5eZrTy8PMDlvx',
urls: {
issuer: 'https://auth-js-test.okta.com',
authorizeUrl: 'https://auth-js-test.okta.com/oauth2/v1/authorize',
userinfoUrl: 'https://auth-js-test.okta.com/oauth2/v1/userinfo',
tokenUrl: 'https://auth-js-test.okta.com/oauth2/v1/token'
},
ignoreSignature: false
})
],
[
'okta-oauth-nonce',
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
],
[
'okta-oauth-state',
'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
]
],
expectedRedirectUrl: 'https://auth-js-test.okta.com/oauth2/v1/authorize?' +
'client_id=NPSfOkH5eZrTy8PMDlvx&' +
'redirect_uri=https%3A%2F%2Fexample.com%2Fredirect&' +
'response_type=code&' +
'response_mode=fragment&' +
'state=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&' +
'nonce=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&' +
'sessionToken=testToken&' +
'code_challenge=' + codeChallenge + '&' +
'code_challenge_method=' + codeChallengeMethod + '&' +
'scope=openid%20email'
});
});

it('sets authorize url for authorization code requests with an authorization server', function() {
mockPKCE();
return oauthUtil.setupRedirect({
Expand Down