Persist scopes in Github provider and add it to the auth response. Fix serialization for Set type in locaStorage.
This commit is contained in:
committed by
Fredrik Adelöw
parent
45ed89b281
commit
4f3e537c3d
@@ -36,6 +36,8 @@ type Options<T> = {
|
||||
/**
|
||||
* AuthSessionStore decorates another SessionManager with a functionality
|
||||
* to store the session in local storage.
|
||||
*
|
||||
* Session is serialized to JSON with special support for following types: Set.
|
||||
*/
|
||||
export class AuthSessionStore<T> implements SessionManager<T> {
|
||||
private readonly manager: SessionManager<T>;
|
||||
@@ -90,7 +92,12 @@ export class AuthSessionStore<T> implements SessionManager<T> {
|
||||
try {
|
||||
const sessionJson = localStorage.getItem(this.storageKey);
|
||||
if (sessionJson) {
|
||||
const session = JSON.parse(sessionJson);
|
||||
const session = JSON.parse(sessionJson, (_key, value) => {
|
||||
if (value?.__type === 'Set') {
|
||||
return new Set(value.__value);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -105,7 +112,18 @@ export class AuthSessionStore<T> implements SessionManager<T> {
|
||||
if (session === undefined) {
|
||||
localStorage.removeItem(this.storageKey);
|
||||
} else {
|
||||
localStorage.setItem(this.storageKey, JSON.stringify(session));
|
||||
localStorage.setItem(
|
||||
this.storageKey,
|
||||
JSON.stringify(session, (_key, value) => {
|
||||
if (value instanceof Set) {
|
||||
return {
|
||||
__type: 'Set',
|
||||
__value: Array.from(value),
|
||||
};
|
||||
}
|
||||
return value;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ export type Options = {
|
||||
providerId: string;
|
||||
secure: boolean;
|
||||
disableRefresh?: boolean;
|
||||
persistScopes?: boolean;
|
||||
baseUrl: string;
|
||||
appOrigin: string;
|
||||
tokenIssuer: TokenIssuer;
|
||||
@@ -105,6 +106,10 @@ export class OAuthProvider implements AuthProviderRouteHandlers {
|
||||
throw new InputError('missing scope parameter');
|
||||
}
|
||||
|
||||
if (this.options.persistScopes) {
|
||||
this.setScopesCookie(res, scope);
|
||||
}
|
||||
|
||||
const nonce = crypto.randomBytes(16).toString('base64');
|
||||
// set a nonce cookie before redirecting to oauth provider
|
||||
this.setNonceCookie(res, nonce);
|
||||
@@ -137,6 +142,14 @@ export class OAuthProvider implements AuthProviderRouteHandlers {
|
||||
req,
|
||||
);
|
||||
|
||||
if (this.options.persistScopes) {
|
||||
const grantedScopes = this.getScopesFromCookie(
|
||||
req,
|
||||
this.options.providerId,
|
||||
);
|
||||
response.providerInfo.scope = grantedScopes;
|
||||
}
|
||||
|
||||
if (!this.options.disableRefresh) {
|
||||
// throw error if missing refresh token
|
||||
if (!refreshToken) {
|
||||
@@ -241,6 +254,21 @@ export class OAuthProvider implements AuthProviderRouteHandlers {
|
||||
});
|
||||
};
|
||||
|
||||
private setScopesCookie = (res: express.Response, scope: string) => {
|
||||
res.cookie(`${this.options.providerId}-scope`, scope, {
|
||||
maxAge: TEN_MINUTES_MS,
|
||||
secure: this.options.secure,
|
||||
sameSite: 'none',
|
||||
domain: this.domain,
|
||||
path: `${this.basePath}/${this.options.providerId}/handler`,
|
||||
httpOnly: true,
|
||||
});
|
||||
};
|
||||
|
||||
private getScopesFromCookie = (req: express.Request, providerId: string) => {
|
||||
return req.cookies[`${providerId}-scope`];
|
||||
};
|
||||
|
||||
private setRefreshTokenCookie = (
|
||||
res: express.Response,
|
||||
refreshToken: string,
|
||||
|
||||
@@ -115,6 +115,7 @@ export function createGithubProvider(
|
||||
|
||||
envProviders[env] = new OAuthProvider(new GithubAuthProvider(opts), {
|
||||
disableRefresh: true,
|
||||
persistScopes: true,
|
||||
providerId: 'github',
|
||||
secure,
|
||||
baseUrl,
|
||||
|
||||
@@ -41,9 +41,11 @@ const ClusterList: FC<{}> = () => {
|
||||
|
||||
const { loading, error, value } = useAsync<ListClusterStatusesResponse>(
|
||||
async () => {
|
||||
const accessToken = await githubAuth.getAccessToken();
|
||||
const userInfo = await api.fetchUserInfo({ accessToken });
|
||||
setGithubUsername(userInfo.login);
|
||||
const accessToken = await githubAuth.getAccessToken(['repo', 'user']);
|
||||
if (!githubUsername) {
|
||||
const userInfo = await api.fetchUserInfo({ accessToken });
|
||||
setGithubUsername(userInfo.login);
|
||||
}
|
||||
return api.listClusters({
|
||||
gitHubToken: accessToken,
|
||||
gitHubUser: githubUsername,
|
||||
|
||||
@@ -52,7 +52,7 @@ const ClusterPage: FC<{}> = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchGithubUserInfo = async () => {
|
||||
const accessToken = await githubAuth.getAccessToken();
|
||||
const accessToken = await githubAuth.getAccessToken(['repo', 'user']);
|
||||
const userInfo = await api.fetchUserInfo({ accessToken });
|
||||
setGithubAccessToken(accessToken);
|
||||
setGithubUsername(userInfo.login);
|
||||
@@ -60,26 +60,27 @@ const ClusterPage: FC<{}> = () => {
|
||||
|
||||
if (!githubAccessToken || !githubUsername) {
|
||||
fetchGithubUserInfo();
|
||||
} else {
|
||||
if (pollingLog) {
|
||||
const interval = setInterval(async () => {
|
||||
const resp = await api.fetchLog({
|
||||
gitHubToken: githubAccessToken,
|
||||
gitHubUser: githubUsername,
|
||||
targetOrg: params.owner,
|
||||
targetRepo: params.repo,
|
||||
});
|
||||
|
||||
setRunStatus(resp.result);
|
||||
setRunLink(resp.link);
|
||||
if (resp.status === 'completed') {
|
||||
setPollingLog(false);
|
||||
setShowProgress(false);
|
||||
}
|
||||
}, 10000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}
|
||||
|
||||
if (pollingLog) {
|
||||
const interval = setInterval(async () => {
|
||||
const resp = await api.fetchLog({
|
||||
gitHubToken: githubAccessToken,
|
||||
gitHubUser: githubUsername,
|
||||
targetOrg: params.owner,
|
||||
targetRepo: params.repo,
|
||||
});
|
||||
|
||||
setRunStatus(resp.result);
|
||||
setRunLink(resp.link);
|
||||
if (resp.status === 'completed') {
|
||||
setPollingLog(false);
|
||||
setShowProgress(false);
|
||||
}
|
||||
}, 10000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
return () => {};
|
||||
}, [pollingLog, api, params, githubAuth, githubAccessToken, githubUsername]);
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@ const ProfileCatalog: FC<{}> = () => {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchGithubUserInfo = async () => {
|
||||
const accessToken = await githubAuth.getAccessToken();
|
||||
const accessToken = await githubAuth.getAccessToken(['repo', 'user']);
|
||||
const userInfo = await api.fetchUserInfo({ accessToken });
|
||||
setGithubAccessToken(accessToken);
|
||||
setGithubUsername(userInfo.login);
|
||||
@@ -140,26 +140,27 @@ const ProfileCatalog: FC<{}> = () => {
|
||||
|
||||
if (!githubAccessToken || !githubUsername) {
|
||||
fetchGithubUserInfo();
|
||||
} else {
|
||||
if (pollingLog) {
|
||||
const interval = setInterval(async () => {
|
||||
const resp = await api.fetchLog({
|
||||
gitHubToken: githubAccessToken,
|
||||
gitHubUser: githubUsername,
|
||||
targetOrg: gitHubOrg,
|
||||
targetRepo: gitHubRepo,
|
||||
});
|
||||
|
||||
setRunStatus(resp.result);
|
||||
setRunLink(resp.link);
|
||||
if (resp.status === 'completed') {
|
||||
setPollingLog(false);
|
||||
setShowProgress(false);
|
||||
}
|
||||
}, 10000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
}
|
||||
|
||||
if (pollingLog) {
|
||||
const interval = setInterval(async () => {
|
||||
const resp = await api.fetchLog({
|
||||
gitHubToken: githubAccessToken,
|
||||
gitHubUser: githubUsername,
|
||||
targetOrg: gitHubOrg,
|
||||
targetRepo: gitHubRepo,
|
||||
});
|
||||
|
||||
setRunStatus(resp.result);
|
||||
setRunLink(resp.link);
|
||||
if (resp.status === 'completed') {
|
||||
setPollingLog(false);
|
||||
setShowProgress(false);
|
||||
}
|
||||
}, 10000);
|
||||
return () => clearInterval(interval);
|
||||
}
|
||||
return () => {};
|
||||
}, [
|
||||
pollingLog,
|
||||
|
||||
@@ -17873,7 +17873,7 @@ tiny-emitter@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
|
||||
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
|
||||
|
||||
tiny-invariant@^1.0.4, tiny-invariant@^1.0.6:
|
||||
tiny-invariant@^1.0.6:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
||||
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
||||
|
||||
Reference in New Issue
Block a user