Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/rude-sloths-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@forgerock/javascript-sdk': minor
---

Add new PingOne signoff, remove unneeded /session call, add flag for iframe
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/DS_Store
**/*.DS_Store
.pnpm-store/*

# Generated code
tmp/
e2e/**/dist
Expand Down Expand Up @@ -30,9 +31,10 @@ packages/javascript-sdk/lib/
.swc
.vite
.env.serve.development
package-lock.json

# Certificates
# *.pem
*.pem

# IDEs
.vscode
Expand Down Expand Up @@ -83,4 +85,4 @@ outputs/*

e2e/mock-api-v2/html/*

vitest.config.*.timestamp*
vitest.config.*.timestamp*
6 changes: 5 additions & 1 deletion e2e/autoscript-apps/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@
<a href="./src/authn-basic/index.html">AuthN: Basic</a><br />
<a href="./src/authn-basic-self-service/">AuthN: Self Service</a><br />
<a href="./src/authn-central-login/index.html">AuthN: Central Login</a><br />
<a href="./src/authn-central-login-no-iframe/index.html">AuthN: Central Login, no iframe</a
><br />
<a href="./src/authn-central-logout/index.html">AuthN: Central Logout Ping</a><br />
<a href="./src/authn-central-logout-wellknown/index.html"
>AuthN: Central Logout with Wellknown</a
><br />
<a href="./src/authn-device-profile/index.html">AuthN: Device Profile</a><br />
<a href="./src/authn-protect/index.html">AuthN: Ping Protect</a><br />
<a href="./src/authn-email-suspend/index.html">AuthN: Email Suspend</a><br />
<a href="./src/authn-recaptcha-enterprise/index.html">AuthN: Recaptcha Enterprise</a><br />
<a href="./src/authn-no-session/index.html">AuthN: No Session</a><br />
<a href="./src/authn-oauth/index.html">AuthN: OAuth</a><br />
<a href="./src/authn-wellknown/index.html">AuthN: WellKnown</a><br />
<a href="./src/authn-platform/index.html">AuthN: Platform Login</a><br />
<a href="./src/authn-second-factor/index.html">AuthN: Second Factor</a><br />
<a href="./src/authn-saml/index.html">AuthN: SAML</a><br />
Expand Down
145 changes: 145 additions & 0 deletions e2e/autoscript-apps/src/authn-central-login-no-iframe/autoscript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/*
* @forgerock/javascript-sdk
*
* autoscript.ts
*
* Copyright (c) 2020 ForgeRock. All rights reserved.
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
// @ts-nocheck
import * as forgerock from '@forgerock/javascript-sdk';
import { delay as rxDelay, map, mergeMap } from 'rxjs/operators';
import { from } from 'rxjs';

function autoscript() {
const delay = 0;

const url = new URL(window.location.href);
const amUrl = url.searchParams.get('amUrl') || 'http://localhost:9443/am';
const preAuthenticated = url.searchParams.get('preAuthenticated') || 'false';
const code = url.searchParams.get('code') || '';
const clientId = url.searchParams.get('clientId');
const client_id = url.searchParams.get('client_id');
const error = url.searchParams.get('error_description') || false;
const realmPath = url.searchParams.get('realmPath') || 'root';
const scope = url.searchParams.get('scope') || 'openid profile me.read';
const state = url.searchParams.get('state') || '';
const acr_values = url.searchParams.get('acr') || 'skipBackgroundRequest';
// in central login we use an auth query param for the return of our mock 401 request
// this is to prevent the evaluation of the page before we have technically authenticated
const auth = url.searchParams.get('auth') || false;

let tokenStore = url.searchParams.get('tokenStore') || 'localStorage';

// Support full redirects by setting storage, rather than rely purely on URL
if (!localStorage.getItem('tokenStore')) {
localStorage.setItem('tokenStore', tokenStore);
} else {
tokenStore = localStorage.getItem('tokenStore');
}

console.log('Configure the SDK');
forgerock.Config.set({
clientId: clientId || client_id || 'CentralLoginOAuthClient',
realmPath,
redirectUri: `${url.origin}/src/${
preAuthenticated === 'false' ? 'authn-central-login' : '_callback'
}/`,
scope,
serverConfig: {
baseUrl: amUrl,
},
tokenStore,
});

if (!code && !state) {
try {
forgerock.SessionManager.logout();
} catch (err) {
// Do nothing
}
}

console.log('Initiate first step with `undefined`');

// Wrapping in setTimeout to give the test time to bind listener to console.log
setTimeout(() => {
from([1])
.pipe(
map(() => {
if (preAuthenticated === 'true') {
console.log('Set mock cookie to represent existing session');
document.cookie = 'iPlanetDirectoryPro=abcd1234; domain=localhost; path=/';
if (code && state) {
window.sessionStorage.setItem(
`FR-SDK-${clientId}`,
JSON.stringify({ responseType: 'code', state, verifier: '1234' }),
);
}
}
return;
}),
rxDelay(delay),
mergeMap((step) => {
let tokens;
// detect when in iframe, throw as error if so
if (window.self !== window.top) {
throw new Error('Loaded_In_Iframe');
} else if (code && state) {
tokens = forgerock.TokenManager.getTokens({
query: { code, state, acr_values },
});
} else {
tokens = forgerock.TokenManager.getTokens({
skipBackgroundRequest: true,
Copy link
Contributor

Choose a reason for hiding this comment

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

i'm concerned with this naming, i just fear another modern | legacy situation where people will read this name and say yeah we want to skip this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As mentioned on Slack. I think this is okay, and unintentially using this option won't do any harm. I've added more information in the JSDoc to cover this option's intention.

login: 'redirect',
query: { acr_values },
});
}
return tokens;
}),
map((tokens) => {
if (tokens.accessToken) {
console.log('OAuth authorization successful');
document.body.innerHTML = '<p class="Logged_In">Login successful</p>';
} else {
throw new Error('Session_Error');
}
}),
rxDelay(delay),
mergeMap(() => {
console.log('Remove cookie');
document.cookie = '';
console.log('Initiate logout');
return forgerock.FRUser.logout();
}),
)
.subscribe({
error: (err) => {
/*
* We added this because Playwright was too fast for the dom element.
* When we make a request to central login we have to force a 401 page to mimick the real life scenario of the page being requested
* If we do this, we append a query param of auth to make sure we don't complete the flow until we are redirected from that page
* By saying we have !auth query param value, we are essentially mimicking the idea that we are waiting for the central login redirect
* to complete the redirect.
*/
if (!auth) {
return;
}
console.log(`Error: ${err.message}`);
document.body.innerHTML = `<p class="Test_Complete">${err.message}</p>`;
localStorage.clear();
},
complete: () => {
console.log('Test script complete');
document.body.innerHTML = `<p class="Test_Complete">Test script complete</p>`;
history.replaceState(null, null, window.location.origin + window.location.pathname);
localStorage.clear();
},
});
}, 250);
}

autoscript();
export default autoscript;
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
</head>

<body>
<!-- script src="/_polyfills/fast-text-encoder.js"></script -->

<script src="autoscript.ts" type="module"></script>
<script type="module" src="./autoscript.ts"></script>
</body>
</html>
86 changes: 49 additions & 37 deletions e2e/autoscript-apps/src/authn-central-login-wellknown/autoscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,49 +16,50 @@ async function autoscript() {
const delay = 0;

const url = new URL(window.location.href);
const preAuthenticated = url.searchParams.get('preAuthenticated') || 'false';
const code = url.searchParams.get('code') || '';
const error = url.searchParams.get('error') || '';
const clientId = url.searchParams.get('clientId');
const client_id = url.searchParams.get('client_id');
const error = url.searchParams.get('error_description') || false;
const realmPath = url.searchParams.get('realmPath') || 'root';
const scope = url.searchParams.get('scope') || 'openid profile me.read';
const state = url.searchParams.get('state') || '';
const acr_values = url.searchParams.get('acr') || 'SpecificTree';
// in central login we use an auth query param for the return of our mock 401 request
// this is to prevent the evaluation of the page before we have technically authenticated
const auth = url.searchParams.get('auth') || false;
const acr_values = url.searchParams.get('acr') || 'SpecificTree';
let wellknown =
url.searchParams.get('wellknown') || 'http://localhost:9443/am/.well-known/oidc-configuration';

let clientId = url.searchParams.get('clientId') || 'CentralLoginOAuthClient';
let realmPath = url.searchParams.get('realmPath') || 'root';
// The `revoke` scope is required for PingOne support
let scope = url.searchParams.get('scope') || 'openid profile me.read revoke';
let wellKnownUrl =
url.searchParams.get('wellKnownUrl') ||
'http://localhost:9443/am/.well-known/oidc-configuration';
let tokenStore = url.searchParams.get('tokenStore') || 'localStorage';

console.log('Configure the SDK');

if (wellKnownUrl) {
localStorage.setItem('wellknown', wellKnownUrl);
localStorage.setItem('clientId', clientId);
localStorage.setItem('realmPath', realmPath);
localStorage.setItem('scope', scope);
// Support full redirects by setting storage, rather than rely purely on URL
if (!localStorage.getItem('tokenStore')) {
localStorage.setItem('tokenStore', tokenStore);
} else {
wellKnownUrl = localStorage.getItem('wellknown');
clientId = localStorage.getItem('clientId');
realmPath = localStorage.getItem('realmPath');
scope = localStorage.getItem('scope');
tokenStore = localStorage.getItem('tokenStore');
}
await forgerock.Config.setAsync({
clientId,

console.log('Configure the SDK');
forgerock.Config.setAsync({
clientId: clientId || client_id || 'CentralLoginOAuthClient',
realmPath,
redirectUri: `${url.origin}/src/authn-central-login-wellknown/`,
redirectUri: `${url.origin}/src/${
preAuthenticated === 'false' ? 'authn-central-login' : '_callback'
}/`,
scope,
serverConfig: {
wellknown: wellKnownUrl,
wellknown,
},
tokenStore,
});

try {
forgerock.SessionManager.logout();
} catch (err) {
// Do nothing
if (!code && !state) {
try {
forgerock.SessionManager.logout();
} catch (err) {
// Do nothing
}
}

console.log('Initiate first step with `undefined`');
Expand All @@ -67,15 +68,29 @@ async function autoscript() {
setTimeout(() => {
from([1])
.pipe(
mergeMap(() => {
map(() => {
if (preAuthenticated === 'true') {
console.log('Set mock cookie to represent existing session');
document.cookie = 'iPlanetDirectoryPro=abcd1234; domain=localhost; path=/';
if (code && state) {
window.sessionStorage.setItem(
`FR-SDK-authflow-${clientId}`,
JSON.stringify({ responseType: 'code', state, verifier: '1234' }),
);
}
}
return;
}),
rxDelay(delay),
mergeMap((step) => {
let tokens;
// detect when in iframe as to not call `/authorize` needlessly
if (window.self !== window.top) {
if (error) {
// Do nothing
return;
} else if (code && state) {
tokens = forgerock.TokenManager.getTokens({
login: 'redirect',
query: { code, state },
query: { code, state, acr_values },
});
} else {
tokens = forgerock.TokenManager.getTokens({
Expand All @@ -98,7 +113,6 @@ async function autoscript() {
console.log('Remove cookie');
document.cookie = '';
console.log('Initiate logout');
// You have to allow specific origins to CORS for OAuth client
return forgerock.FRUser.logout();
}),
)
Expand All @@ -116,14 +130,12 @@ async function autoscript() {
}
console.log(`Error: ${err.message}`);
document.body.innerHTML = `<p class="Test_Complete">${err.message}</p>`;
localStorage.clear();
},
complete: () => {
console.log('Test script complete');
document.body.innerHTML = `<p class="Test_Complete">Test script complete</p>`;
localStorage.removeItem('wellknown');
localStorage.removeItem('clientId');
localStorage.removeItem('realmPath');
localStorage.removeItem('scope');
localStorage.clear();
},
});
}, 250);
Expand Down
11 changes: 4 additions & 7 deletions e2e/autoscript-apps/src/authn-central-logout/autoscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ async function autoscript() {

const logout = async () => {
try {
await forgerock.FRUser.logout({
logoutRedirectUri: `${window.location.origin}${window.location.pathname}`,
});
await forgerock.FRUser.logout();
} catch (error) {
console.error(error);
}
Expand All @@ -67,7 +65,7 @@ async function autoscript() {
* Passing no arguments or a key-value of `login: 'embedded'` means
* the app handles authentication locally.
*/
await forgerock.TokenManager.getTokens({ login: 'redirect' });
await forgerock.TokenManager.getTokens({ login: 'redirect', skipBackgroundRequest: true });
const user = await forgerock.UserManager.getCurrentUser();
showUser(user);
});
Expand All @@ -80,10 +78,9 @@ async function autoscript() {
await forgerock.Config.setAsync({
clientId: '724ec718-c41c-4d51-98b0-84a583f450f9', // e.g. 'ForgeRockSDKClient'
redirectUri: `${window.location.origin}${window.location.pathname}`, // Redirect back to your app, e.g. 'https://sdkapp.example.com:8443'
scope: 'openid profile email name revoke', // e.g. 'openid profile email address phone me.read'
scope: 'openid profile email revoke', // e.g. 'openid profile email address phone me.read'
serverConfig: {
wellknown:
'https://auth.pingone.ca/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/.well-known/openid-configuration',
wellknown: 'https://pingone.petrov.ca/as/.well-known/openid-configuration',
},
realmPath: '', // e.g. 'alpha' or 'root'
});
Expand Down
8 changes: 8 additions & 0 deletions e2e/autoscript-apps/src/authn-central-logout/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
/>
<style>
#Error {
display: none;
}
#Fatal {
display: none;
}
</style>
</head>
<script type="module" src="autoscript.ts"></script>
<body>
Expand Down
Loading
Loading