set and use access-token cookie

This commit is contained in:
Erik Larsson
2021-02-18 03:35:45 +01:00
parent 813c6a4f20
commit f18b51d932
3 changed files with 53 additions and 2 deletions
@@ -11,6 +11,7 @@ Caveat: as of writing this, Backstage does not refresh the identity token so eve
```typescript
// packages/backend/src/index.ts from a create-app deployment
import cookieParser from 'cookie-parser';
import { Request, Response, NextFunction } from 'express';
import { IdentityClient } from '@backstage/plugin-auth-backend';
@@ -30,7 +31,9 @@ async function main() {
next: NextFunction,
) => {
try {
const token = IdentityClient.getBearerToken(req.headers.authorization);
const token =
IdentityClient.getBearerToken(req.headers.authorization) ||
req.cookies['access-token'];
req.user = await identity.authenticate(token);
next();
} catch (error) {
@@ -39,6 +42,7 @@ async function main() {
};
const apiRouter = Router();
apiRouter.use(cookieParser());
// The auth route must be publically available as it is used during login
apiRouter.use('/auth', await auth(authEnv));
// Only authenticated requests are allowed to the routes below
@@ -16,6 +16,7 @@
import express from 'express';
import crypto from 'crypto';
import { JWT } from 'jose';
import { URL } from 'url';
import {
AuthProviderRouteHandlers,
@@ -127,6 +128,9 @@ export class OAuthAdapter implements AuthProviderRouteHandlers {
}
await this.populateIdentity(response.backstageIdentity);
if (response.backstageIdentity?.idToken) {
this.setAccessTokenCookie(res, response.backstageIdentity?.idToken);
}
// post message back to popup if successful
return postMessageResponse(res, this.options.appOrigin, {
@@ -151,6 +155,7 @@ export class OAuthAdapter implements AuthProviderRouteHandlers {
return;
}
this.removeAccessTokenCookie(res);
if (!this.options.disableRefresh) {
// remove refresh token cookie before logout
this.removeRefreshTokenCookie(res);
@@ -190,6 +195,9 @@ export class OAuthAdapter implements AuthProviderRouteHandlers {
);
await this.populateIdentity(response.backstageIdentity);
if (response.backstageIdentity?.idToken) {
this.setAccessTokenCookie(res, response.backstageIdentity?.idToken);
}
if (
response.providerInfo.refreshToken &&
@@ -246,6 +254,20 @@ export class OAuthAdapter implements AuthProviderRouteHandlers {
return req.cookies[`${providerId}-scope`];
};
private setAccessTokenCookie = (
res: express.Response,
accessToken: string,
) => {
res.cookie(`access-token`, accessToken, {
expires: new Date(JWT.decode(accessToken).exp * 1000),
secure: this.options.secure,
sameSite: 'lax',
domain: this.options.cookieDomain,
path: '/api',
httpOnly: true,
});
};
private setRefreshTokenCookie = (
res: express.Response,
refreshToken: string,
@@ -260,6 +282,17 @@ export class OAuthAdapter implements AuthProviderRouteHandlers {
});
};
private removeAccessTokenCookie = (res: express.Response) => {
res.cookie(`access-token`, '', {
maxAge: 0,
secure: this.options.secure,
sameSite: 'lax',
domain: this.options.cookieDomain,
path: '/api',
httpOnly: true,
});
};
private removeRefreshTokenCookie = (res: express.Response) => {
res.cookie(`${this.options.providerId}-refresh-token`, '', {
maxAge: 0,
+15 -1
View File
@@ -82,9 +82,15 @@ export async function createRouter({
const { kind, namespace, name } = req.params;
try {
const token =
getBearerToken(req.headers.authorization) ||
req.cookies['access-token'];
const entity = (await (
await fetch(
`${catalogUrl}/entities/by-name/${kind}/${namespace}/${name}`,
{
headers: token ? { Authorization: `Bearer ${token}` } : {},
},
)
).json()) as Entity;
@@ -109,7 +115,11 @@ export async function createRouter({
const catalogUrl = await discovery.getBaseUrl('catalog');
const triple = [kind, namespace, name].map(encodeURIComponent).join('/');
const catalogRes = await fetch(`${catalogUrl}/entities/by-name/${triple}`);
const token =
getBearerToken(req.headers.authorization) || req.cookies['access-token'];
const catalogRes = await fetch(`${catalogUrl}/entities/by-name/${triple}`, {
headers: token ? { Authorization: `Bearer ${token}` } : {},
});
if (!catalogRes.ok) {
const catalogResText = await catalogRes.text();
res.status(catalogRes.status);
@@ -202,3 +212,7 @@ export async function createRouter({
return router;
}
function getBearerToken(header?: string): string | undefined {
return header?.match(/(?:Bearer|Basic)\s+(\S+)/i)?.[1];
}