set and use access-token cookie
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user