Skip to content

Commit

Permalink
Merge pull request #163 from adrienne-deriv/add-state-parameter-new
Browse files Browse the repository at this point in the history
Add state parameter and whitelist deriv.dev for cookies
  • Loading branch information
amam-deriv authored Feb 26, 2025
2 parents cb74efb + fb62d0c commit e873a2a
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 12 deletions.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,44 @@ Once the legacy tokens are sent to the consumer apps, the library assumes that t
Note : The callback page does NOT handle authorize calls. Its sole purpose is to do the access token exchange and return back the legacy tokens to the consumer apps.
## State parameter
You can pass in an additional state parameter for payloads to `requestOidcAuthentication` or `requestSilentOidcAuthentication`, which will be carried over to the `Callback` component. You can perform things like passing in an additional `redirect_to` metadata to inform `Callback` page where to redirect to next after authentication is completed:
```
requestOidcAuthentication({
redirectCallbackUri: `${window.location.origin}/callback`,
state: {
redirect_to: '/tradershub/home'
}
});
```
And within the `Callback` component, it will return the state from the `onSignInSuccess` callback function:
```
const CallbackPage = () => {
const { updateLoginAccounts } = useAuthContext();
return (
<Callback
onSignInSuccess={(tokens, state) => {
const accounts = transformAccountsFromResponseBody(tokens);
updateLoginAccounts(accounts);
const redirectTo = (state as Record<string, any>)?.redirect_to;
if (redirectTo) {
window.location.href = redirectTo;
} else {
window.location.href = '/';
}
}}
/>
);
};
```
## Logout Flow
This logout process combines two parts: clearing OAuth session cookies through the OAuth2Logout function and running custom cleanup logic specific to your app (like clearing user accounts or tokens). Let’s break it down step-by-step:
Expand Down
13 changes: 7 additions & 6 deletions src/components/Callback/Callback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import './Callback.scss';

type CallbackProps = {
/** callback function triggerred when `requestOidcToken` is successful. Use this only when you want to request the legacy tokens yourself, otherwise pass your callback to `onSignInSuccess` prop instead */
onRequestOidcTokenSuccess?: (accessToken: string) => void;
onRequestOidcTokenSuccess?: (accessToken: string, state: unknown) => void;
/** callback function triggered when the OIDC authentication flow is successful */
onSignInSuccess?: (tokens: LegacyTokens) => void;
onSignInSuccess?: (tokens: LegacyTokens, state: unknown) => void;
/** callback function triggered when sign-in encounters an error */
onSignInError?: (error: Error) => void;
/** URI to redirect to the callback page. This is where you should pass the callback page URL in your app .e.g. https://app.deriv.com/callback or https://smarttrader.deriv.com/en/callback */
Expand Down Expand Up @@ -62,18 +62,19 @@ export const Callback = ({

const fetchTokens = useCallback(async () => {
try {
const { accessToken } = await requestOidcToken({
const { accessToken, userManager } = await requestOidcToken({
redirectCallbackUri,
postLogoutRedirectUri,
});
const user = await userManager.getUser();

if (accessToken) {
onRequestOidcTokenSuccess?.(accessToken);
onRequestOidcTokenSuccess?.(accessToken, user?.state);

const legacyTokens = await requestLegacyToken(accessToken);

onSignInSuccess?.(legacyTokens);
const domains = ['deriv.com', 'binary.sx', 'pages.dev', 'localhost'];
onSignInSuccess?.(legacyTokens, user?.state);
const domains = ['deriv.com', 'deriv.dev', 'binary.sx', 'pages.dev', 'localhost'];
const currentDomain = window.location.hostname.split('.').slice(-2).join('.');
if (domains.includes(currentDomain)) {
Cookies.set('logged_state', 'true', {
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useOAuth2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const useOAuth2 = (OAuth2GrowthBookConfig: OAuth2GBConfig, WSLogoutAndRed

const onMessage = (event: MessageEvent) => {
if (event.data === 'logout_complete') {
const domains = ['deriv.com', 'binary.sx', 'pages.dev', 'localhost'];
const domains = ['deriv.com', 'deriv.dev', 'binary.sx', 'pages.dev', 'localhost'];
const currentDomain = window.location.hostname.split('.').slice(-2).join('.');
if (domains.includes(currentDomain)) {
Cookies.set('logged_state', 'false', {
Expand Down
23 changes: 18 additions & 5 deletions src/oidc/oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ type RequestOidcAuthenticationOptions = {
redirectCallbackUri?: string;
postLoginRedirectUri?: string;
postLogoutRedirectUri?: string;
state?: Record<string, any>;
};

type requestOidcSilentAuthenticationOptions = {
redirectCallbackUri?: string;
redirectSilentCallbackUri: string;
state?: Record<string, any>;
};

type RequestOidcTokenOptions = {
Expand Down Expand Up @@ -100,6 +102,7 @@ export const fetchOidcConfiguration = async (): Promise<OidcConfiguration> => {
* @param options.redirectCallbackUri - The callback page URI to redirect back
* @param options.postLoginRedirectUri - The URI to redirect after the callback page. This is where you usually pass the page URL where you initiated the login flow
* @param options.postLogoutRedirectUri - The URI where the application should redirect after processing the logout
* * @param options.state - An optional payload you can pass to the authentication flow. This will allow OIDC to carry your state in the callback page, where you can perform additional redirection actions based on the state
*
* @returns Promise that resolves to an object containing the UserManager instance
* @throws {OIDCError} With type AuthenticationRequestFailed if the authentication request fails
Expand All @@ -110,7 +113,10 @@ export const fetchOidcConfiguration = async (): Promise<OidcConfiguration> => {
* const { userManager } = await requestOidcAuthentication({
* redirectCallbackUri: 'https://smarttrader.deriv.com/en/callback',
* postLoginRedirectUri: 'https://smarttrader.deriv.com/en/trading',
* postLogoutRedirectUri: https://smarttrader.deriv.com/en/trading''
* postLogoutRedirectUri: https://smarttrader.deriv.com/en/trading',
* state: {
* redirect_to: '/tradershub/home'
* }
* });
* } catch (error) {
* // Handle authentication request error
Expand All @@ -123,7 +129,7 @@ export const fetchOidcConfiguration = async (): Promise<OidcConfiguration> => {
* - The post login/logout redirect URIs are stored in local storage as `config.post_login_redirect_uri` and `config.post_logout_redirect_uri`
*/
export const requestOidcAuthentication = async (options: RequestOidcAuthenticationOptions) => {
const { redirectCallbackUri, postLoginRedirectUri, postLogoutRedirectUri } = options;
const { redirectCallbackUri, postLoginRedirectUri, postLogoutRedirectUri, state } = options;

// If the post login redirect URI is not specified, redirect the user back to where the OIDC authentication is initiated
// This will be used later by the Callback component to redirect back to where the OIDC flow is initiated
Expand All @@ -141,6 +147,7 @@ export const requestOidcAuthentication = async (options: RequestOidcAuthenticati
extraQueryParams: {
brand: 'deriv',
},
state,
});
return { userManager };
} catch (error) {
Expand All @@ -157,6 +164,7 @@ export const requestOidcAuthentication = async (options: RequestOidcAuthenticati
*
* @param options - Configuration options for the OIDC silent authentication request
* @param options.redirectSilentCallbackUri - The silent callback page URI which will be rendered in an iframe to check login status
* @param options.state - An optional payload you can pass to the silent authentication flow. This will allow OIDC to carry your state in the callback page, where you can perform additional redirection actions based on the state
*
* @returns Promise that resolves to an object containing the UserManager instance
* @throws {OIDCError} With type AuthenticationRequestFailed if the authentication request fails
Expand All @@ -166,6 +174,9 @@ export const requestOidcAuthentication = async (options: RequestOidcAuthenticati
* try {
* const { userManager } = await requestOidcSilentAuthentication({
* redirectCallbackUri: 'https://smarttrader.deriv.com/en/silent-callback',
* * state: {
* redirect_to: '/tradershub/home'
* }
* });
* } catch (error) {
* // Handle authentication request error
Expand All @@ -176,7 +187,7 @@ export const requestOidcAuthentication = async (options: RequestOidcAuthenticati
* - An iframe will be generated and embedded in the page, which will send postMessage events to the parent window to indicate the login status
*/
export const requestOidcSilentAuthentication = async (options: requestOidcSilentAuthenticationOptions) => {
const { redirectCallbackUri, redirectSilentCallbackUri } = options;
const { redirectCallbackUri, redirectSilentCallbackUri, state } = options;

try {
const userManager = await createUserManager({
Expand All @@ -188,6 +199,7 @@ export const requestOidcSilentAuthentication = async (options: requestOidcSilent
extraQueryParams: {
brand: 'deriv',
},
state,
silentRequestTimeoutInSeconds: 60000,
});
return { userManager };
Expand Down Expand Up @@ -241,6 +253,7 @@ export const requestOidcToken = async (options: RequestOidcTokenOptions) => {

return {
accessToken: user?.access_token,
userManager,
};
} catch (error) {
console.error('unable to request access tokens: ', error);
Expand Down Expand Up @@ -409,7 +422,7 @@ export const OAuth2Logout = async (options: OAuth2LogoutOptions) => {
};
const onMessage = (event: MessageEvent) => {
if (event.data === 'logout_complete') {
const domains = ['deriv.com', 'binary.sx', 'pages.dev', 'localhost'];
const domains = ['deriv.com', 'deriv.dev', 'binary.sx', 'pages.dev', 'localhost'];
const currentDomain = window.location.hostname.split('.').slice(-2).join('.');
if (domains.includes(currentDomain)) {
Cookies.set('logged_state', 'false', {
Expand Down Expand Up @@ -525,7 +538,7 @@ export const handlePostLogout = (callbackFunction: () => void) => {
const sessionStorageKey = `oidc.user:${serverUrl}:${appId}`;

if (!window.sessionStorage.getItem(sessionStorageKey)) {
const domains = ['deriv.com', 'binary.sx', 'pages.dev', 'localhost'];
const domains = ['deriv.com', 'deriv.dev', 'binary.sx', 'pages.dev', 'localhost'];
const currentDomain = window.location.hostname.split('.').slice(-2).join('.');
if (domains.includes(currentDomain)) {
Cookies.set('logged_state', 'false', {
Expand Down

0 comments on commit e873a2a

Please sign in to comment.