[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:
Wyatt Johnson
2020-08-06 09:20:46 -06:00
committed by GitHub
parent b029a01bd9
commit 2bbfbc7ddd
7 changed files with 39 additions and 12 deletions
+10 -3
View File
@@ -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) {