Skip to content

Commit

Permalink
doc(oidc-client-demo): repair demo
Browse files Browse the repository at this point in the history
  • Loading branch information
guillaume-chervet committed Feb 23, 2024
1 parent fa33e72 commit cf92792
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 114 deletions.
2 changes: 1 addition & 1 deletion examples/oidc-client-demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
<script crossorigin type="module" src="/src/index.tsx"></script>
</body>
</html>
246 changes: 134 additions & 112 deletions examples/oidc-client-demo/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,128 +1,137 @@
import { OidcClient } from '@axa-fr/oidc-client';
import {OidcClient} from "@axa-fr/oidc-client";

class Router
{ getCustomHistory(){
const generateKey = () =>
Math.random()
.toString(36)
.substr(2, 6);

// Exported only for test
type WindowInternal = Window & {
CustomEvent?: new <T>(typeArg: string, eventInitDict?: CustomEventInit<T>) => CustomEvent<T>;
Event: typeof Event;
};

type IPrototype = {
prototype: any;
};

type InitCustomEventParams<T = any> = {
bubbles: boolean;
cancelable: boolean;
detail: T;
};

// IE Polyfill for CustomEvent
const CreateEvent = (windowInternal: Window, documentInternal: Document) => (
event: string,
params: InitCustomEventParams,
): CustomEvent => {
// @ts-ignore
if (typeof windowInternal.CustomEvent === 'function') {
// @ts-ignore
return new windowInternal.CustomEvent(event, params);
}
const paramsToFunction = params || { bubbles: false, cancelable: false, detail: undefined };
const evt: CustomEvent = documentInternal.createEvent('CustomEvent');
evt.initCustomEvent(event, paramsToFunction.bubbles, paramsToFunction.cancelable, paramsToFunction.detail);
const generateKey = () =>
Math.random()
.toString(36)
.substr(2, 6);

// Exported only for test
type WindowInternal = Window & {
CustomEvent?: new <T>(typeArg: string, eventInitDict?: CustomEventInit<T>) => CustomEvent<T>;
Event: typeof Event;
};

type IPrototype = {
prototype: any;
};

type InitCustomEventParams<T = any> = {
bubbles: boolean;
cancelable: boolean;
detail: T;
};

// IE Polyfill for CustomEvent
const CreateEvent = (windowInternal: Window, documentInternal: Document) => (
event: string,
params: InitCustomEventParams,
): CustomEvent => {
// @ts-ignore
if (typeof windowInternal.CustomEvent === 'function') {
// @ts-ignore
(evt as CustomEvent & IPrototype).prototype = windowInternal.Event.prototype;
return evt;
};
return new windowInternal.CustomEvent(event, params);
}
const paramsToFunction = params || { bubbles: false, cancelable: false, detail: undefined };
const evt: CustomEvent = documentInternal.createEvent('CustomEvent');
evt.initCustomEvent(event, paramsToFunction.bubbles, paramsToFunction.cancelable, paramsToFunction.detail);
// @ts-ignore
(evt as CustomEvent & IPrototype).prototype = windowInternal.Event.prototype;
return evt;
};

type WindowHistoryState = typeof window.history.state;
type WindowHistoryState = typeof window.history.state;

type CustomHistory = {
replaceState(url?: string | null, stateHistory?: WindowHistoryState): void;
}
type CustomHistory = {
replaceState(url?: string | null, stateHistory?: WindowHistoryState): void;
}

const getHistory = (
windowInternal: WindowInternal,
CreateEventInternal: (event: string, params?: InitCustomEventParams) => CustomEvent,
generateKeyInternal: typeof generateKey,
): CustomHistory => {
return {
replaceState: (url?: string | null, stateHistory?: WindowHistoryState): void => {
const key = generateKeyInternal();
const state = stateHistory || windowInternal.history.state;
// @ts-ignore
windowInternal.history.replaceState({ key, state }, null, url);
windowInternal.dispatchEvent(CreateEventInternal('popstate'));
},
};
const getHistory = (
windowInternal: WindowInternal,
CreateEventInternal: (event: string, params?: InitCustomEventParams) => CustomEvent,
generateKeyInternal: typeof generateKey,
): CustomHistory => {
return {
replaceState: (url?: string | null, stateHistory?: WindowHistoryState): void => {
const key = generateKeyInternal();
const state = stateHistory || windowInternal.history.state;
// @ts-ignore
windowInternal.history.replaceState({ key, state }, null, url);
windowInternal.dispatchEvent(CreateEventInternal('popstate'));
},
};
};

// @ts-ignore
// @ts-ignore
const getCustomHistory = () => getHistory(window, CreateEvent(window, document), generateKey);
return getCustomHistory();
}
return getCustomHistory();
}

}

const router = new Router();
// @ts-ignore
export const execute = () => {

document.body.innerHTML = `<div id="my-vanilla-app"></div>`;
const element = document.getElementById("my-vanilla-app");
const router = new Router();

export const configuration = {
client_id: 'interactive.public.short',
redirect_uri: window.location.origin + '/#/authentication/callback',
silent_redirect_uri: window.location.origin + '/#/authentication/silent-callback',
scope: 'openid profile email api offline_access',
authority: 'https://demo.duendesoftware.com',
refresh_time_before_tokens_expiration_in_second: 40,
service_worker_relative_url:'/OidcServiceWorker.js',
service_worker_only: true,
};
const element = document.getElementById("root");

const href = window.location.href;
const configuration = {
client_id: 'interactive.public.short',
redirect_uri: window.location.origin + '/#/authentication/callback',
silent_redirect_uri: window.location.origin + '/#/authentication/silent-callback',
scope: 'openid profile email api offline_access',
authority: 'https://demo.duendesoftware.com',
refresh_time_before_tokens_expiration_in_second: 40,
service_worker_relative_url:'/OidcServiceWorker.js',
service_worker_only: true,
};

const vanillaOidc = OidcClient.getOrCreate(() => fetch)(configuration);
const href = window.location.href;

console.log(href);
const vanillaOidc = OidcClient.getOrCreate(() => fetch)(configuration);

if(href.includes(configuration.redirect_uri)){
// @ts-ignore
element.innerHTML = `<div>
console.log(href);

if(href.includes(configuration.redirect_uri)){
// @ts-ignore
element.innerHTML = `<div>
<h1>@axa-fr/oidc-client demo</h1>
<h2>Loading callback</h2>
</div>`;
vanillaOidc.loginCallbackAsync().then(()=>{
router.getCustomHistory().replaceState("/");
// @ts-ignore
window.logout = () => vanillaOidc.logoutAsync();
const tokens = vanillaOidc.tokens;
// @ts-ignore
element.innerHTML = `<div>
vanillaOidc.loginCallbackAsync().then(()=>{
router.getCustomHistory().replaceState("/");
// @ts-ignore
function logout() {
vanillaOidc.logoutAsync();
}
const tokens = vanillaOidc.tokens;
// @ts-ignore
element.innerHTML = `<div>
<h1>@axa-fr/oidc-client demo</h1>
<button onclick="window.logout()">Logout</button>
<button id="logout" >Logout</button>
<h2>Authenticated</h2>
<pre>${JSON.stringify(tokens,null,'\t')}</pre>
</div>`;
});
}

vanillaOidc.tryKeepExistingSessionAsync().then(() => {
const tokens = vanillaOidc.tokens;
if(tokens){

// @ts-ignore
window.logout = () => vanillaOidc.logoutAsync();
// @ts-ignore
element.innerHTML = `<div>
// @ts-ignore
window.document.getElementById('logout').addEventListener('click',logout);
});
}

vanillaOidc.tryKeepExistingSessionAsync().then(() => {
const tokens = vanillaOidc.tokens;
if(tokens){

// @ts-ignore
function logout () {
vanillaOidc.logoutAsync();
}
// @ts-ignore
element.innerHTML = `<div>
<h1>@axa-fr/oidc-client demo</h1>
<button onclick="window.logout()">Logout</button>
<button id="logout">Logout</button>
<p>Game, let's try to make an XSS attacks to retrieve original tokens !</p>
<p>You may think servcie worker mode is not secure like said here <a href="https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#payload-new-flow">https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#payload-new-flow</a>
So let try to hack it !
Expand All @@ -131,31 +140,44 @@ vanillaOidc.tryKeepExistingSessionAsync().then(() => {
Content-Security-Policy: script-src 'self'
</pre>
and setting up the redirect_uri and redirect_silent_uri at the top level of your javascript application before any XSS attack could accur.
Security is always good a cursor level to adjsute and a set of good practices.
Security is always good a cursor level to adjsut and a set of good practices.
</p>
<textarea id="xsshack">alert('XSS');</textarea>
<button onclick="eval(document.getElementById('xsshack').value)">Hack</button>
<button id="buttonxsshack" >Hack</button>
<h2>Authenticated</h2>
<pre>${JSON.stringify(tokens,null,'\t')}</pre>
</div>`;
}
else {
// @ts-ignore
window.login= () => {
// @ts-ignore
element.innerHTML = `<div>
window.document.getElementById('logout').addEventListener('click',logout);
// @ts-ignore
window.document.getElementById('buttonxsshack').addEventListener('click',()=> {
// @ts-ignore
eval(document.getElementById('xsshack').value)
});

}
else {
// @ts-ignore
function login() {
// @ts-ignore
element.innerHTML = `<div>
<h1>@axa-fr/oidc-client demo</h1>
<h2>Loading</h2>
</div>`;
vanillaOidc.loginAsync("/");
};
// @ts-ignore
element.innerHTML = `<div>
vanillaOidc.loginAsync("/");
}

// @ts-ignore
element.innerHTML = `<div>
<h1>@axa-fr/oidc-client demo</h1>
<button onclick="window.login()">Login</button>
<button id="login" >Login</button>
</div>`;
}
});
// @ts-ignore
document.getElementById('login').addEventListener('click',login);
}
});

};

execute();
2 changes: 1 addition & 1 deletion examples/oidc-client-demo/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default defineConfig({
},
server: {
headers: {
//"Content-Security-Policy": "script-src 'self';",
"Content-Security-Policy": "script-src 'self' 'unsafe-eval';",
},
},
});

0 comments on commit cf92792

Please sign in to comment.