Skip to content

Commit 996ea3f

Browse files
committed
First release of yoga plugin
1 parent ecedac7 commit 996ea3f

File tree

10 files changed

+435
-0
lines changed

10 files changed

+435
-0
lines changed

.changeset/shy-laws-guess.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@labdigital/federated-token-yoga": major
3+
---
4+
5+
First release of yoga plugin

packages/yoga/.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist/**

packages/yoga/.eslintrc.cjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
extends: ["../../.eslintrc.cjs"],
3+
};

packages/yoga/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Federated Token - Yoga Plugin
2+
3+
[![npm](https://img.shields.io/npm/v/@labdigital/federated-token.svg)](https://www.npmjs.com/package/@labdigital/federated-token)
4+
5+
This packages provides a GraphQL Yoga plugin so that for Yoga can be used as a
6+
federated service.
7+
8+
## Usage
9+
10+
- `useFederatedToken()` - The plugin for federated services that reads
11+
the token passed in the `x-access-token` header and stores it on the context
12+
as `federatedToken`.
13+
14+
When a federated services creates a new token (when non exist) it can also
15+
return a refresh token in the `x-refresh-token` header. The gateway will then
16+
encrypt all refresh tokens and encrypt them before passing them to the client
17+
as `x-refresh-token` header.

packages/yoga/package.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"name": "@labdigital/federated-token-yoga",
3+
"version": "0.0.0",
4+
"description": "Federated token plugin for GraphQL Yoga",
5+
"module": "./dist/index.js",
6+
"main": "./dist/index.cjs",
7+
"types": "./dist/index.d.ts",
8+
"type": "module",
9+
"exports": {
10+
".": {
11+
"import": "./dist/index.js",
12+
"require": "./dist/index.cjs"
13+
}
14+
},
15+
"keywords": [
16+
"graphql",
17+
"authentication",
18+
"yoga"
19+
],
20+
"author": "Lab Digital <[email protected]>",
21+
"license": "MIT",
22+
"repository": {
23+
"type": "git",
24+
"url": "https://github.com/labd/node-federated-token"
25+
},
26+
"publishConfig": {
27+
"access": "public"
28+
},
29+
"scripts": {
30+
"build": "tsup",
31+
"test": "vitest run",
32+
"test:ci": "vitest run --coverage",
33+
"tsc": "tsc --noEmit",
34+
"format": "eslint src --fix && prettier --write .",
35+
"lint": "eslint src && prettier --check ."
36+
},
37+
"files": [
38+
"dist",
39+
"src"
40+
],
41+
"dependencies": {
42+
"@labdigital/federated-token": "workspace:*",
43+
"graphql-yoga": "^5.9.0"
44+
},
45+
"devDependencies": {
46+
"@types/express": "^4.17.21",
47+
"@typescript-eslint/eslint-plugin": "^7.18.0",
48+
"@vitest/coverage-v8": "1.6.0",
49+
"eslint": "^8.57.1",
50+
"eslint-plugin-unused-imports": "^4.1.4",
51+
"node-mocks-http": "^1.16.1",
52+
"prettier": "^3.3.3",
53+
"tsup": "^8.3.0",
54+
"typescript": "^5.6.3",
55+
"vitest": "1.6.0"
56+
},
57+
"peerDependencies": {
58+
"graphql": ">= 16.6.0"
59+
}
60+
}

packages/yoga/src/index.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { Plugin } from "graphql-yoga";
2+
import { FederatedToken } from "@labdigital/federated-token";
3+
4+
type FederatedTokenContext = {
5+
federatedToken: FederatedToken;
6+
};
7+
8+
export const useFederatedToken = <T extends FederatedTokenContext>(): Plugin<
9+
object,
10+
T
11+
> => ({
12+
onRequest: ({ request, serverContext }) => {
13+
// Initialize FederatedToken and add it to the context
14+
const federatedToken = serverContext.federatedToken ?? new FederatedToken();
15+
16+
// Retrieve tokens from headers using the serverContext
17+
const accessToken = request.headers.get("x-access-token") as string;
18+
const refreshToken = request.headers.get("x-refresh-token") as string;
19+
20+
if (accessToken) {
21+
federatedToken.deserializeAccessToken(accessToken);
22+
}
23+
24+
if (refreshToken) {
25+
federatedToken.loadRefreshToken(refreshToken);
26+
}
27+
28+
serverContext.federatedToken = federatedToken;
29+
},
30+
31+
onResponse: async ({ response, serverContext }) => {
32+
const { federatedToken } = serverContext;
33+
if (!federatedToken) return;
34+
35+
// Check if the tokens were modified and set headers accordingly
36+
if (
37+
federatedToken.isAccessTokenModified() ||
38+
federatedToken.isValueModified()
39+
) {
40+
const serializedToken = federatedToken.serializeAccessToken();
41+
if (serializedToken) {
42+
response.headers.set("X-Access-Token", serializedToken);
43+
}
44+
}
45+
46+
if (federatedToken.isRefreshTokenModified()) {
47+
const serializedRefreshToken = federatedToken.dumpRefreshToken();
48+
if (serializedRefreshToken) {
49+
response.headers.set("X-Refresh-Token", serializedRefreshToken);
50+
}
51+
}
52+
},
53+
});

packages/yoga/tsconfig.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"include": ["src/**/*"]
4+
}

packages/yoga/tsup.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { defineConfig } from "tsup";
2+
3+
export default defineConfig([
4+
{
5+
entry: ["src/index.ts"],
6+
clean: true,
7+
splitting: false,
8+
dts: true,
9+
sourcemap: true,
10+
format: ["esm", "cjs"],
11+
outDir: "dist",
12+
},
13+
]);

packages/yoga/vitest.config.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { defineConfig } from "vitest/config";
2+
import path from "path";
3+
4+
export default defineConfig({
5+
test: {
6+
coverage: {
7+
provider: "v8",
8+
all: true,
9+
include: ["src/**/*.ts"],
10+
reportsDirectory: "./test-reports/",
11+
},
12+
passWithNoTests: true,
13+
},
14+
15+
resolve: {
16+
alias: {
17+
"~src": path.join(__dirname, "src"),
18+
},
19+
},
20+
});

0 commit comments

Comments
 (0)