Skip to content

Commit aa8bcc9

Browse files
committed
Add some comments to explain logic
1 parent 07c2edc commit aa8bcc9

File tree

3 files changed

+32
-11
lines changed

3 files changed

+32
-11
lines changed

src/gateway.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@ type GatewayOptions = {
1717
source: TokenSource;
1818
};
1919

20+
/**
21+
* This plugin is used to authenticate requests coming into the gateway. It
22+
* reads the tokens from the request (using the token source provided),
23+
* validates them. When sending back the response it will also set the tokens
24+
* via the tokenSource on the response if the downstream services have
25+
* created/modified them.
26+
*/
2027
export class GatewayAuthPlugin<TContext extends PublicFederatedTokenContext>
2128
implements ApolloServerPlugin, GraphQLRequestListener<TContext>
2229
{
@@ -55,7 +62,8 @@ export class GatewayAuthPlugin<TContext extends PublicFederatedTokenContext>
5562
const token = contextValue.federatedToken;
5663

5764
// Only load the access token if there is no refresh token. If a refresh
58-
// token is present then we assume a refresh is happening
65+
// token is present then we assume a refresh is happening and the
66+
// accessToken is expired/invalid anyway
5967
if (accessToken && !refreshToken) {
6068
try {
6169
await token.loadAccessJWT(this.signer, accessToken, fingerprint);
@@ -96,6 +104,10 @@ export class GatewayAuthPlugin<TContext extends PublicFederatedTokenContext>
96104
const token = contextValue?.federatedToken;
97105
const { req: request, res: response } = contextValue;
98106

107+
// Downstream services modified the tokens, so create a new JWT and set
108+
// it on the response
109+
// TODO: We should optimize this, if only the values are modified then
110+
// we shouldn't have to create a new nested JWE
99111
if (token?.isAccessTokenModified() || token?.isValueModified()) {
100112
const { accessToken, fingerprint } = await token.createAccessJWT(
101113
this.signer

src/jwt.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,28 +26,32 @@ export class TokenExpiredError extends Error {}
2626
export class TokenInvalidError extends Error {}
2727

2828
export class PublicFederatedToken extends FederatedToken {
29-
async createAccessJWT(signer: TokenSigner) {
30-
// Find the expire time of the first token that expires
31-
const values = Object.values(this.tokens);
32-
const sorted = values.sort((a, b) => a.exp - b.exp);
33-
const exp = sorted[0].exp;
3429

30+
// Create the access JWT. This JWT is send to the client. It is send as
31+
// signed token (not encrypted). The jwe attribute is encrypted however.
32+
// This is all done when the GraphQL gateway sends the response back to the
33+
// client.
34+
async createAccessJWT(signer: TokenSigner) {
35+
const exp = this.getExpireTime()
3536
const fingerprint = generateFingerprint();
37+
const subject = await signer.getSubject(this);
38+
3639
const payload: JWTPayload = {
37-
exp: exp,
38-
jwe: await signer.encryptObject(this.tokens),
3940
...this.values,
41+
exp,
42+
sub: subject,
43+
jwe: await signer.encryptObject(this.tokens),
4044
_fingerprint: hashFingerprint(fingerprint),
4145
};
4246

43-
const token = await signer.signJWT(payload, exp);
44-
47+
const token = await signer.signJWT(payload);
4548
return {
4649
accessToken: token,
4750
fingerprint: fingerprint,
4851
};
4952
}
5053

54+
5155
async loadAccessJWT(
5256
signer: TokenSigner,
5357
value: string,
@@ -78,7 +82,7 @@ export class PublicFederatedToken extends FederatedToken {
7882
}
7983

8084
this.tokens = await signer.decryptObject(payload.jwe);
81-
const knownKeys = ["jwe", "iat", "exp", "aud", "iss", "_fingerprint"];
85+
const knownKeys = ["jwe", "iat", "exp", "aud", "sub", "jti", "iss", "_fingerprint"];
8286
for (const k in payload) {
8387
if (!knownKeys.includes(k)) {
8488
this.values[k] = payload[k];

src/plugin.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ import {
55
} from "@apollo/server";
66
import { FederatedToken, FederatedTokenContext } from "./token.js";
77

8+
9+
// FederatedAuthPlugin is an Apollo plugin which should be used by all
10+
// downstream services. It reads the information from the request headers as
11+
// set by the GatewayAuthPlugin and sets the information on the response
12+
// headers for the GatewayAuthPlugin to be consumed.
813
export class FederatedAuthPlugin<TContext extends FederatedTokenContext>
914
implements ApolloServerPlugin, GraphQLRequestListener<TContext>
1015
{

0 commit comments

Comments
 (0)