@ogcio/api-auth 5.1.1 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,9 +1,18 @@
1
1
  import type { FastifyInstance } from "fastify";
2
+ import { type JSONWebKeySet } from "jose";
2
3
  type ExtractedUserData = {
3
4
  userId: string;
4
5
  organizationId?: string;
5
6
  isM2MApplication: boolean;
6
7
  accessToken: string;
8
+ signInMethod?: string;
9
+ };
10
+ type StoreLocalJwkSet = (keySet: JSONWebKeySet) => Promise<void>;
11
+ export type CheckPermissionsPluginOpts = {
12
+ jwkEndpoint: string;
13
+ oidcEndpoint: string;
14
+ getLocalJwksFn?: () => JSONWebKeySet | undefined;
15
+ storeLocalJwkSetFn?: StoreLocalJwkSet;
7
16
  };
8
17
  declare module "fastify" {
9
18
  interface FastifyRequest {
@@ -11,16 +20,9 @@ declare module "fastify" {
11
20
  }
12
21
  }
13
22
  export declare const ensureUserCanAccessUser: (loggedUserData: ExtractedUserData | undefined, requestedUserId: string) => ExtractedUserData;
14
- export declare const checkPermissions: (authHeader: string, config: {
15
- jwkEndpoint: string;
16
- oidcEndpoint: string;
17
- }, requiredPermissions: string[], matchConfig?: {
23
+ export declare const checkPermissions: (authHeader: string, config: CheckPermissionsPluginOpts, requiredPermissions: string[], matchConfig?: {
18
24
  method: string;
19
25
  }) => Promise<ExtractedUserData>;
20
- export type CheckPermissionsPluginOpts = {
21
- jwkEndpoint: string;
22
- oidcEndpoint: string;
23
- };
24
26
  export declare const checkPermissionsPlugin: (app: FastifyInstance, opts: CheckPermissionsPluginOpts) => Promise<void>;
25
27
  declare const _default: (app: FastifyInstance, opts: CheckPermissionsPluginOpts) => Promise<void>;
26
28
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAK7E,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAIF,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;KAC9B;CACF;AA2BD,eAAO,MAAM,uBAAuB,mBAClB,iBAAiB,GAAG,SAAS,mBAC5B,MAAM,KACtB,iBAUF,CAAC;AAEF,eAAO,MAAM,gBAAgB,eACf,MAAM,UACV;IACN,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,uBACoB,MAAM,EAAE;;MAE5B,OAAO,CAAC,iBAAiB,CAmC3B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,eAAO,MAAM,sBAAsB,QAC5B,eAAe,QACd,0BAA0B,kBA2BjC,CAAC;8BA5BK,eAAe,QACd,0BAA0B;AA6BlC,wBAEG;AAEH,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAE7E,OAAO,EAEL,KAAK,aAAa,EAMnB,MAAM,MAAM,CAAC;AAGd,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAIF,KAAK,gBAAgB,GAAG,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAEjE,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,aAAa,GAAG,SAAS,CAAC;IACjD,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;CACvC,CAAC;AAEF,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;KAC9B;CACF;AAiED,eAAO,MAAM,uBAAuB,mBAClB,iBAAiB,GAAG,SAAS,mBAC5B,MAAM,KACtB,iBAUF,CAAC;AAEF,eAAO,MAAM,gBAAgB,eACf,MAAM,UACV,0BAA0B,uBACb,MAAM,EAAE;;MAE5B,OAAO,CAAC,iBAAiB,CAsC3B,CAAC;AAEF,eAAO,MAAM,sBAAsB,QAC5B,eAAe,QACd,0BAA0B,kBA2BjC,CAAC;8BA5BK,eAAe,QACd,0BAA0B;AA6BlC,wBAEG;AAEH,cAAc,yBAAyB,CAAC;AACxC,cAAc,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { httpErrors } from "@fastify/sensible";
2
2
  import { getErrorMessage } from "@ogcio/shared-errors";
3
3
  import fp from "fastify-plugin";
4
- import { createRemoteJWKSet, jwtVerify } from "jose";
4
+ import { createLocalJWKSet, createRemoteJWKSet, jwtVerify, } from "jose";
5
5
  import { getMapFromScope, validatePermission } from "./utils.js";
6
6
  const extractBearerToken = (authHeader) => {
7
7
  const [type, token] = authHeader.split(" ");
@@ -10,10 +10,44 @@ const extractBearerToken = (authHeader) => {
10
10
  }
11
11
  return token;
12
12
  };
13
+ /**
14
+ * Reference: https://docs.logto.io/docs/recipes/protect-your-api/node/
15
+ * @param token
16
+ * @param config
17
+ * @returns JWTPayload
18
+ */
13
19
  const decodeLogtoToken = async (token, config) => {
14
- // Reference: https://docs.logto.io/docs/recipes/protect-your-api/node/
15
- const jwks = createRemoteJWKSet(new URL(config.jwkEndpoint));
16
- const { payload } = await jwtVerify(token, jwks, {
20
+ let jwksSet;
21
+ // check if local JSONWebKeySet retrieval function is provided
22
+ if (config.getLocalJwksFn) {
23
+ try {
24
+ jwksSet = config.getLocalJwksFn();
25
+ }
26
+ catch {
27
+ // just ignoring the error to avoid changes in
28
+ // decodeLogtoToken behaviours
29
+ }
30
+ }
31
+ let resolverFn;
32
+ if (!jwksSet) {
33
+ const remoteSet = createRemoteJWKSet(new URL(config.jwkEndpoint));
34
+ const remoteJwks = remoteSet.jwks();
35
+ if (config.storeLocalJwkSetFn && remoteJwks) {
36
+ try {
37
+ await config.storeLocalJwkSetFn(remoteJwks);
38
+ }
39
+ catch {
40
+ // just ignoring the error to avoid changes in
41
+ // method behaviours
42
+ }
43
+ }
44
+ resolverFn = remoteSet;
45
+ }
46
+ else {
47
+ const localJwkSet = createLocalJWKSet(jwksSet);
48
+ resolverFn = localJwkSet;
49
+ }
50
+ const { payload } = await jwtVerify(token, resolverFn, {
17
51
  issuer: config.oidcEndpoint,
18
52
  });
19
53
  return payload;
@@ -30,7 +64,7 @@ export const ensureUserCanAccessUser = (loggedUserData, requestedUserId) => {
30
64
  export const checkPermissions = async (authHeader, config, requiredPermissions, matchConfig = { method: "OR" }) => {
31
65
  const token = extractBearerToken(authHeader);
32
66
  const payload = await decodeLogtoToken(token, config);
33
- const { scope, sub, aud, client_id: clientId, } = payload;
67
+ const { scope, sub, aud, client_id: clientId, signInMethod, } = payload;
34
68
  const scopesMap = getMapFromScope(scope);
35
69
  const grantAccess = matchConfig.method === "AND"
36
70
  ? requiredPermissions.every((p) => validatePermission(p, scopesMap))
@@ -46,6 +80,7 @@ export const checkPermissions = async (authHeader, config, requiredPermissions,
46
80
  organizationId: organizationId,
47
81
  accessToken: token,
48
82
  isM2MApplication: sub === clientId,
83
+ signInMethod,
49
84
  };
50
85
  };
51
86
  export const checkPermissionsPlugin = async (app, opts) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,23 +1,19 @@
1
1
  {
2
2
  "name": "@ogcio/api-auth",
3
- "version": "5.1.1",
3
+ "version": "5.2.0",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "dependencies": {
7
- "@aws-sdk/client-kms": "^3.709.0",
8
- "@fastify/sensible": "6.0.1",
9
- "@ogcio/shared-errors": "1.0.0",
10
- "fastify": "^5.1.0",
11
- "fastify-plugin": "^5.0.1",
12
- "jose": "^5.9.6"
7
+ "@aws-sdk/client-kms": "^3.936.0",
8
+ "@fastify/sensible": "6.0.3",
9
+ "@ogcio/shared-errors": "1.1.0",
10
+ "fastify": "^5.6.2",
11
+ "fastify-plugin": "^5.1.0",
12
+ "jose": "^6.1.2"
13
13
  },
14
14
  "scripts": {
15
15
  "build": "rm -rf dist tsconfig.prod.tsbuildinfo tsconfig.tsbuildinfo && tsc -p tsconfig.prod.json",
16
16
  "test": "vitest run --coverage --outputFile=results.xml",
17
17
  "prepublishOnly": "npm i && npm run build && npm run test"
18
- },
19
- "devDependencies": {
20
- "@types/node": "22.10.1",
21
- "typescript": "^5.7.2"
22
18
  }
23
19
  }
package/src/index.ts CHANGED
@@ -2,7 +2,15 @@ import { httpErrors } from "@fastify/sensible";
2
2
  import { getErrorMessage } from "@ogcio/shared-errors";
3
3
  import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
4
4
  import fp from "fastify-plugin";
5
- import { createRemoteJWKSet, jwtVerify } from "jose";
5
+ import {
6
+ type FlattenedJWSInput,
7
+ type JSONWebKeySet,
8
+ type JWSHeaderParameters,
9
+ type JWTPayload,
10
+ createLocalJWKSet,
11
+ createRemoteJWKSet,
12
+ jwtVerify,
13
+ } from "jose";
6
14
  import { getMapFromScope, validatePermission } from "./utils.js";
7
15
 
8
16
  type ExtractedUserData = {
@@ -10,10 +18,20 @@ type ExtractedUserData = {
10
18
  organizationId?: string;
11
19
  isM2MApplication: boolean;
12
20
  accessToken: string;
21
+ signInMethod?: string;
13
22
  };
14
23
 
15
24
  type MatchConfig = { method: "AND" | "OR" };
16
25
 
26
+ type StoreLocalJwkSet = (keySet: JSONWebKeySet) => Promise<void>;
27
+
28
+ export type CheckPermissionsPluginOpts = {
29
+ jwkEndpoint: string;
30
+ oidcEndpoint: string;
31
+ getLocalJwksFn?: () => JSONWebKeySet | undefined;
32
+ storeLocalJwkSetFn?: StoreLocalJwkSet;
33
+ };
34
+
17
35
  declare module "fastify" {
18
36
  interface FastifyRequest {
19
37
  userData?: ExtractedUserData;
@@ -30,18 +48,56 @@ const extractBearerToken = (authHeader: string) => {
30
48
  return token;
31
49
  };
32
50
 
51
+ /**
52
+ * Reference: https://docs.logto.io/docs/recipes/protect-your-api/node/
53
+ * @param token
54
+ * @param config
55
+ * @returns JWTPayload
56
+ */
33
57
  const decodeLogtoToken = async (
34
58
  token: string,
35
- config: {
36
- jwkEndpoint: string;
37
- oidcEndpoint: string;
38
- },
39
- ) => {
40
- // Reference: https://docs.logto.io/docs/recipes/protect-your-api/node/
41
- const jwks = createRemoteJWKSet(new URL(config.jwkEndpoint));
42
- const { payload } = await jwtVerify(token, jwks, {
59
+ config: CheckPermissionsPluginOpts,
60
+ ): Promise<JWTPayload> => {
61
+ let jwksSet: JSONWebKeySet | undefined;
62
+
63
+ // check if local JSONWebKeySet retrieval function is provided
64
+ if (config.getLocalJwksFn) {
65
+ try {
66
+ jwksSet = config.getLocalJwksFn();
67
+ } catch {
68
+ // just ignoring the error to avoid changes in
69
+ // decodeLogtoToken behaviours
70
+ }
71
+ }
72
+
73
+ let resolverFn:
74
+ | undefined
75
+ | ((
76
+ protectedHeader?: JWSHeaderParameters,
77
+ token?: FlattenedJWSInput,
78
+ ) => Promise<CryptoKey>);
79
+
80
+ if (!jwksSet) {
81
+ const remoteSet = createRemoteJWKSet(new URL(config.jwkEndpoint));
82
+ const remoteJwks = remoteSet.jwks();
83
+ if (config.storeLocalJwkSetFn && remoteJwks) {
84
+ try {
85
+ await config.storeLocalJwkSetFn(remoteJwks);
86
+ } catch {
87
+ // just ignoring the error to avoid changes in
88
+ // method behaviours
89
+ }
90
+ }
91
+ resolverFn = remoteSet;
92
+ } else {
93
+ const localJwkSet = createLocalJWKSet(jwksSet);
94
+ resolverFn = localJwkSet;
95
+ }
96
+
97
+ const { payload } = await jwtVerify(token, resolverFn, {
43
98
  issuer: config.oidcEndpoint,
44
99
  });
100
+
45
101
  return payload;
46
102
  };
47
103
 
@@ -62,10 +118,7 @@ export const ensureUserCanAccessUser = (
62
118
 
63
119
  export const checkPermissions = async (
64
120
  authHeader: string,
65
- config: {
66
- jwkEndpoint: string;
67
- oidcEndpoint: string;
68
- },
121
+ config: CheckPermissionsPluginOpts,
69
122
  requiredPermissions: string[],
70
123
  matchConfig = { method: "OR" },
71
124
  ): Promise<ExtractedUserData> => {
@@ -76,11 +129,13 @@ export const checkPermissions = async (
76
129
  sub,
77
130
  aud,
78
131
  client_id: clientId,
132
+ signInMethod,
79
133
  } = payload as {
80
134
  scope: string;
81
135
  sub: string;
82
136
  aud: string;
83
137
  client_id: string;
138
+ signInMethod?: string;
84
139
  };
85
140
  const scopesMap = getMapFromScope(scope);
86
141
 
@@ -102,14 +157,10 @@ export const checkPermissions = async (
102
157
  organizationId: organizationId,
103
158
  accessToken: token,
104
159
  isM2MApplication: sub === clientId,
160
+ signInMethod,
105
161
  };
106
162
  };
107
163
 
108
- export type CheckPermissionsPluginOpts = {
109
- jwkEndpoint: string;
110
- oidcEndpoint: string;
111
- };
112
-
113
164
  export const checkPermissionsPlugin = async (
114
165
  app: FastifyInstance,
115
166
  opts: CheckPermissionsPluginOpts,