mirror of
https://github.com/wassname/talk.git
synced 2026-06-28 20:08:37 +08:00
[CORL-1242] Ensure SSO Access Tokens are not persisted (#3080)
* fix: ensure access tokens from sso are not persisted * fix: increment storage key field to bust existing usage Co-authored-by: Vinh <vinh@vinh.tech>
This commit is contained in:
@@ -3,7 +3,7 @@ import { Claims, computeExpiresIn, parseAccessTokenClaims } from "./helpers";
|
||||
/**
|
||||
* ACCESS_TOKEN_KEY is the key in storage where the accessToken is stored.
|
||||
*/
|
||||
const ACCESS_TOKEN_KEY = "coral:v1:accessToken";
|
||||
const ACCESS_TOKEN_KEY = "coral:v2:accessToken";
|
||||
|
||||
export interface AuthState {
|
||||
/**
|
||||
@@ -19,7 +19,7 @@ export interface AuthState {
|
||||
|
||||
export type AccessTokenProvider = () => string | undefined;
|
||||
|
||||
function parseAccessToken(accessToken: string) {
|
||||
export function parseAccessToken(accessToken: string) {
|
||||
// Try to parse the access token claims.
|
||||
const claims = parseAccessTokenClaims(accessToken);
|
||||
if (!claims) {
|
||||
@@ -59,6 +59,13 @@ export function retrieveAccessToken() {
|
||||
}
|
||||
|
||||
export function storeAccessToken(accessToken: string) {
|
||||
// Parse the access token that's trying to be stored now. If it can't be
|
||||
// parsed, it can't be stored.
|
||||
const parsed = parseAccessToken(accessToken);
|
||||
if (!parsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Update the access token in storage.
|
||||
localStorage.setItem(ACCESS_TOKEN_KEY, accessToken);
|
||||
@@ -69,7 +76,7 @@ export function storeAccessToken(accessToken: string) {
|
||||
}
|
||||
|
||||
// Return the parsed access token.
|
||||
return parseAccessToken(accessToken);
|
||||
return parsed;
|
||||
}
|
||||
|
||||
export function deleteAccessToken() {
|
||||
|
||||
@@ -63,7 +63,10 @@ export interface CoralContext {
|
||||
eventEmitter: EventEmitter2;
|
||||
|
||||
/** Clear session data. */
|
||||
clearSession: (nextAccessToken?: string | null) => Promise<void>;
|
||||
clearSession: (
|
||||
nextAccessToken?: string | null,
|
||||
ephemeral?: boolean
|
||||
) => Promise<void>;
|
||||
|
||||
/** Change locale and rerender */
|
||||
changeLocale: (locale: LanguageCode) => Promise<void>;
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
AccessTokenProvider,
|
||||
AuthState,
|
||||
deleteAccessToken,
|
||||
parseAccessToken,
|
||||
retrieveAccessToken,
|
||||
storeAccessToken,
|
||||
} from "../auth";
|
||||
@@ -160,7 +161,10 @@ function createManagedCoralContextProvider(
|
||||
}
|
||||
|
||||
// This is called every time a user session starts or ends.
|
||||
private clearSession = async (nextAccessToken?: string) => {
|
||||
private clearSession = async (
|
||||
nextAccessToken?: string,
|
||||
ephemeral?: boolean
|
||||
) => {
|
||||
// Clear session storage.
|
||||
void this.state.context.sessionStorage.clear();
|
||||
|
||||
@@ -169,7 +173,9 @@ function createManagedCoralContextProvider(
|
||||
|
||||
// Parse the claims/token and update storage.
|
||||
const auth = nextAccessToken
|
||||
? storeAccessToken(nextAccessToken)
|
||||
? ephemeral
|
||||
? parseAccessToken(nextAccessToken)
|
||||
: storeAccessToken(nextAccessToken)
|
||||
: deleteAccessToken();
|
||||
|
||||
// Create the new environment.
|
||||
|
||||
@@ -4,18 +4,28 @@ import { CoralContext } from "coral-framework/lib/bootstrap";
|
||||
import { createMutation } from "coral-framework/lib/relay";
|
||||
|
||||
interface SetAccessTokenInput {
|
||||
/**
|
||||
* accessToken is the new access token to use for this session or persisted if
|
||||
* `ephemeral` is falsy.
|
||||
*/
|
||||
accessToken: string | null;
|
||||
|
||||
/**
|
||||
* ephemeral when set to true will ensure that the passed token is not
|
||||
* persisted to storage.
|
||||
*/
|
||||
ephemeral?: boolean;
|
||||
}
|
||||
|
||||
const SetAccessTokenMutation = createMutation(
|
||||
"setAccessToken",
|
||||
async (
|
||||
environment: Environment,
|
||||
input: SetAccessTokenInput,
|
||||
{ accessToken, ephemeral }: SetAccessTokenInput,
|
||||
{ clearSession }: CoralContext
|
||||
) => {
|
||||
// Clear current session, as we are starting a new one.
|
||||
await clearSession(input.accessToken);
|
||||
await clearSession(accessToken, ephemeral);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ it("Listens to event and calls setAccessToken", () => {
|
||||
|
||||
const setAccessToken = createSinonStub(
|
||||
(s) => s.throws(),
|
||||
(s) => s.withArgs({ accessToken }).returns(null)
|
||||
(s) => s.withArgs({ accessToken, ephemeral: true }).returns(null)
|
||||
);
|
||||
|
||||
createRenderer().render(
|
||||
|
||||
@@ -16,7 +16,7 @@ export class OnPymLogin extends Component<Props> {
|
||||
|
||||
// Sets comment id through pym.
|
||||
props.pym.onMessage("login", (accessToken) => {
|
||||
void this.props.setAccessToken({ accessToken });
|
||||
void this.props.setAccessToken({ accessToken, ephemeral: true });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { commitLocalUpdate, Environment } from "relay-runtime";
|
||||
|
||||
import { parseQuery } from "coral-common/utils";
|
||||
import { AuthState, storeAccessToken } from "coral-framework/lib/auth";
|
||||
import { AuthState, parseAccessToken } from "coral-framework/lib/auth";
|
||||
import { CoralContext } from "coral-framework/lib/bootstrap";
|
||||
import { getExternalConfig } from "coral-framework/lib/externalConfig";
|
||||
import { createAndRetain, initLocalBaseState } from "coral-framework/lib/relay";
|
||||
@@ -20,7 +20,8 @@ export default async function initLocalState(
|
||||
const config = await getExternalConfig(context.pym);
|
||||
if (config) {
|
||||
if (config.accessToken) {
|
||||
auth = storeAccessToken(config.accessToken);
|
||||
// Access tokens passed via the config should not be persisted.
|
||||
auth = parseAccessToken(config.accessToken);
|
||||
}
|
||||
// append body class name if set in config.
|
||||
if (config.bodyClassName) {
|
||||
|
||||
Reference in New Issue
Block a user