@ogcio/api-auth 5.1.2 → 5.2.1

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,4 +1,5 @@
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;
@@ -6,22 +7,22 @@ type ExtractedUserData = {
6
7
  accessToken: string;
7
8
  signInMethod?: string;
8
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;
16
+ };
9
17
  declare module "fastify" {
10
18
  interface FastifyRequest {
11
19
  userData?: ExtractedUserData;
12
20
  }
13
21
  }
14
22
  export declare const ensureUserCanAccessUser: (loggedUserData: ExtractedUserData | undefined, requestedUserId: string) => ExtractedUserData;
15
- export declare const checkPermissions: (authHeader: string, config: {
16
- jwkEndpoint: string;
17
- oidcEndpoint: string;
18
- }, requiredPermissions: string[], matchConfig?: {
23
+ export declare const checkPermissions: (authHeader: string, config: CheckPermissionsPluginOpts, requiredPermissions: string[], matchConfig?: {
19
24
  method: string;
20
25
  }) => Promise<ExtractedUserData>;
21
- export type CheckPermissionsPluginOpts = {
22
- jwkEndpoint: string;
23
- oidcEndpoint: string;
24
- };
25
26
  export declare const checkPermissionsPlugin: (app: FastifyInstance, opts: CheckPermissionsPluginOpts) => Promise<void>;
26
27
  declare const _default: (app: FastifyInstance, opts: CheckPermissionsPluginOpts) => Promise<void>;
27
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;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,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,CAsC3B,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, signInMethod } = 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,7 +80,7 @@ export const checkPermissions = async (authHeader, config, requiredPermissions,
46
80
  organizationId: organizationId,
47
81
  accessToken: token,
48
82
  isM2MApplication: sub === clientId,
49
- signInMethod
83
+ signInMethod,
50
84
  };
51
85
  };
52
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,15 +1,15 @@
1
1
  {
2
2
  "name": "@ogcio/api-auth",
3
- "version": "5.1.2",
3
+ "version": "5.2.1",
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",
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 = {
@@ -15,6 +23,15 @@ type ExtractedUserData = {
15
23
 
16
24
  type MatchConfig = { method: "AND" | "OR" };
17
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
+
18
35
  declare module "fastify" {
19
36
  interface FastifyRequest {
20
37
  userData?: ExtractedUserData;
@@ -31,18 +48,56 @@ const extractBearerToken = (authHeader: string) => {
31
48
  return token;
32
49
  };
33
50
 
51
+ /**
52
+ * Reference: https://docs.logto.io/docs/recipes/protect-your-api/node/
53
+ * @param token
54
+ * @param config
55
+ * @returns JWTPayload
56
+ */
34
57
  const decodeLogtoToken = async (
35
58
  token: string,
36
- config: {
37
- jwkEndpoint: string;
38
- oidcEndpoint: string;
39
- },
40
- ) => {
41
- // Reference: https://docs.logto.io/docs/recipes/protect-your-api/node/
42
- const jwks = createRemoteJWKSet(new URL(config.jwkEndpoint));
43
- 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, {
44
98
  issuer: config.oidcEndpoint,
45
99
  });
100
+
46
101
  return payload;
47
102
  };
48
103
 
@@ -63,10 +118,7 @@ export const ensureUserCanAccessUser = (
63
118
 
64
119
  export const checkPermissions = async (
65
120
  authHeader: string,
66
- config: {
67
- jwkEndpoint: string;
68
- oidcEndpoint: string;
69
- },
121
+ config: CheckPermissionsPluginOpts,
70
122
  requiredPermissions: string[],
71
123
  matchConfig = { method: "OR" },
72
124
  ): Promise<ExtractedUserData> => {
@@ -77,7 +129,7 @@ export const checkPermissions = async (
77
129
  sub,
78
130
  aud,
79
131
  client_id: clientId,
80
- signInMethod
132
+ signInMethod,
81
133
  } = payload as {
82
134
  scope: string;
83
135
  sub: string;
@@ -105,15 +157,10 @@ export const checkPermissions = async (
105
157
  organizationId: organizationId,
106
158
  accessToken: token,
107
159
  isM2MApplication: sub === clientId,
108
- signInMethod
160
+ signInMethod,
109
161
  };
110
162
  };
111
163
 
112
- export type CheckPermissionsPluginOpts = {
113
- jwkEndpoint: string;
114
- oidcEndpoint: string;
115
- };
116
-
117
164
  export const checkPermissionsPlugin = async (
118
165
  app: FastifyInstance,
119
166
  opts: CheckPermissionsPluginOpts,