diff --git a/jest.server.js b/jest.server.js index aa809144c..9c36526fc 100644 --- a/jest.server.js +++ b/jest.server.js @@ -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', diff --git a/lib/browser/browser.js b/lib/browser/browser.js index f443a8088..1b98bdcd4 100644 --- a/lib/browser/browser.js +++ b/lib/browser/browser.js @@ -31,6 +31,8 @@ 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'; this.options = { url: util.removeTrailingSlash(url), clientId: args.clientId, @@ -38,7 +40,7 @@ function OktaAuthBuilder(args) { 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, diff --git a/lib/token.js b/lib/token.js index f76cf6ae1..e8ed0ecbb 100644 --- a/lib/token.js +++ b/lib/token.js @@ -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, @@ -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) { @@ -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); } @@ -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'; diff --git a/test/spec/browser.js b/test/spec/browser.js new file mode 100644 index 000000000..578b2d5cc --- /dev/null +++ b/test/spec/browser.js @@ -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'); + }); + }) + }); +}); \ No newline at end of file diff --git a/test/spec/token.js b/test/spec/token.js index 9ec85d416..0365f7e7e 100644 --- a/test/spec/token.js +++ b/test/spec/token.js @@ -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({