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 idp parameter #46

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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
22 changes: 11 additions & 11 deletions dist/appid.min.js

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions dist/appid.umd.min.js

Large diffs are not rendered by default.

26 changes: 19 additions & 7 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class AppID {
* @param {Object} [options.popup] - The popup configuration.
* @param {Number} options.popup.height - The popup height.
* @param {Number} options.popup.width - The popup width.
* @param {string} options.idp - An enabled IdP name or "appid_anon" for anonymous login. If left empty, App ID login widget will be returned with all enabled IdPs.
* @returns {Promise<void>}
* @throws {AppIDError} For missing required params.
* @throws {RequestError} Any errors during a HTTP request.
Expand All @@ -66,7 +67,7 @@ class AppID {
* });
*
*/
async init({clientId, discoveryEndpoint, popup = {height: window.screen.height * .80, width: 400}}) {
async init({clientId, discoveryEndpoint, popup = {height: window.screen.height * .80, width: 400}, idp}) {
if (!clientId) {
throw new AppIDError(constants.MISSING_CLIENT_ID);
}
Expand All @@ -79,6 +80,7 @@ class AppID {
await this.openIdConfigResource.init({discoveryEndpoint, requestHandler: this.request});
this.popup.init(popup);
this.clientId = clientId;
this.idp = idp;
this.initialized = true;
}

Expand All @@ -93,6 +95,8 @@ class AppID {
/**
* This will open a sign in widget in a popup which will prompt the user to enter their credentials.
* After a successful sign in, the popup will close and tokens are returned.
* @param {Object} options
* @param {string} options.idp - An enabled IdP name or "appid_anon" for anonymous login. If left empty, idp parameter that was set during initialization will be used
* @returns {Promise<Tokens>} The tokens of the authenticated user.
* @throws {PopupError} "Popup closed" - The user closed the popup before authentication was completed.
* @throws {TokenError} Any token validation error.
Expand All @@ -101,7 +105,8 @@ class AppID {
* @example
* const {accessToken, accessTokenPayload, idToken, idTokenPayload} = await appID.signin();
*/
async signin() {
async signin(options) {
const { idp } = options || {};
this._validateInitalize();
const endpoint = this.openIdConfigResource.getAuthorizationEndpoint();
let origin = this.window.location.origin;
Expand All @@ -111,7 +116,8 @@ class AppID {
return this.utils.performOAuthFlowAndGetTokens({
origin,
endpoint,
clientId: this.clientId
clientId: this.clientId,
idp: idp || this.idp,
});
}

Expand All @@ -120,6 +126,8 @@ class AppID {
* This will attempt to authenticate the user in a hidden iframe.
* You will need to [enable Cloud Directory SSO]{@link https://cloud.ibm.com/docs/services/appid?topic=appid-single-page#spa-silent-login}.
* Sign in will be successful only if the user has previously signed in using Cloud Directory and their session is not expired.
* @param {Object} options
* @param {string} options.idp - An enabled IdP name or "appid_anon" for anonymous login. If left empty, idp parameter that was set during initialization will be used
* @returns {Promise<Tokens>} The tokens of the authenticated user.
* @throws {OAuthError} Any errors from the server according to the [OAuth spec]{@link https://tools.ietf.org/html/rfc6749#section-4.1.2.1}. e.g. {error: 'access_denied', description: 'User not signed in'}
* @throws {IFrameError} "Silent sign-in timed out" - The iframe will close after 5 seconds if authentication could not be completed.
Expand All @@ -128,14 +136,16 @@ class AppID {
* @example
* const {accessToken, accessTokenPayload, idToken, idTokenPayload} = await appID.silentSignin();
*/
async silentSignin() {
async silentSignin(options) {
const { idp } = options || {};
this._validateInitalize();
const endpoint = this.openIdConfigResource.getAuthorizationEndpoint();
const {codeVerifier, nonce, state, url} = this.utils.getAuthParamsAndUrl({
clientId: this.clientId,
origin: this.window.origin,
prompt: constants.PROMPT,
endpoint
endpoint,
idp: idp || this.idp,
});

this.iframe.open(url);
Expand Down Expand Up @@ -220,7 +230,8 @@ class AppID {
userId,
origin: this.window.origin,
clientId: this.clientId,
endpoint
endpoint,
idp: this.idp,
});
}

Expand Down Expand Up @@ -259,7 +270,8 @@ class AppID {
origin: this.window.origin,
clientId: this.clientId,
endpoint,
changeDetailsCode
changeDetailsCode,
idp: this.idp,
});
}

Expand Down
10 changes: 7 additions & 3 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class Utils {
return {codeVerifier, codeChallenge, state, nonce};
}

getAuthParamsAndUrl({clientId, origin, prompt, endpoint, userId, changeDetailsCode}) {
getAuthParamsAndUrl({clientId, origin, prompt, endpoint, userId, changeDetailsCode, idp}) {
const {codeVerifier, codeChallenge, state, nonce} = this.getPKCEFields();
let authParams = {
client_id: clientId,
Expand All @@ -71,6 +71,10 @@ class Utils {
authParams.code = changeDetailsCode;
}

if (idp) {
authParams.idp = idp;
}

const url = endpoint + '?' + this.buildParams(authParams);
return {
codeVerifier,
Expand All @@ -80,8 +84,8 @@ class Utils {
};
}

async performOAuthFlowAndGetTokens({userId, origin, clientId, endpoint, changeDetailsCode}) {
const {codeVerifier, state, nonce, url} = this.getAuthParamsAndUrl({userId, origin, clientId, endpoint, changeDetailsCode});
async performOAuthFlowAndGetTokens({userId, origin, clientId, endpoint, changeDetailsCode, idp}) {
const {codeVerifier, state, nonce, url} = this.getAuthParamsAndUrl({userId, origin, clientId, endpoint, changeDetailsCode, idp});

this.popup.open();
this.popup.navigate(url);
Expand Down
10 changes: 9 additions & 1 deletion test/utilsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('Utils tests', () => {
});

it('should succeed - performOAuthFlowAndGetTokens', async () => {
let res = await utils.performOAuthFlowAndGetTokens({userId: 'userId', origin: 'origin', clientId: 'clientId'});
let res = await utils.performOAuthFlowAndGetTokens({userId: 'userId', origin: 'origin', clientId: 'clientId', idp: 'idp'});
});

it('should return auth params with prompt', () => {
Expand Down Expand Up @@ -60,6 +60,14 @@ describe('Utils tests', () => {
assert.notInclude(res.url, 'prompt');
});

it('should return auth params with idp', () => {
let res = utils.getAuthParamsAndUrl({clientId: '1234', origin: 'http://origin.com', endpoint: 'auth', idp: 'customIdp'});
assert.exists(res.codeVerifier, 'returned code verifier');
assert.exists(res.nonce, 'returned nonce');
assert.exists(res.state, 'returned state');
assert.include(res.url, 'idp=customIdp');
});

it('should return tokens', async () => {
let params = {
authCode: 'code',
Expand Down