@cemiar/auth-sdk 1.0.15 → 1.0.17

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/README.md CHANGED
@@ -1,149 +1,149 @@
1
- # @cemiar/auth-sdk
2
-
3
- Reusable helpers for integrating Cemiar Auth in web front-ends and Node.js APIs.
4
-
5
- This package bundles:
6
-
7
- - A browser-friendly Cemiar Auth client with email codes, Microsoft login support, token storage, refresh and logout helpers
8
- - Axios utilities to attach automatic Authorization headers and refresh tokens on 401 responses
9
- - A Hapi JWT strategy helper that fetches Cemiar Auth signing keys from JWKS and validates incoming requests
10
-
11
- ## Installation
12
-
13
- ```bash
14
- npm install @cemiar/auth-sdk
15
- ```
16
-
17
- When working inside this monorepo, you can add it via a file reference:
18
-
19
- ```bash
20
- npm install @cemiar/auth-sdk@file:../packages/cemiar-auth-sdk
21
- ```
22
-
23
- Make sure the host project also has the required peer dependencies installed (`@hapi/hapi`, `hapi-auth-jwt2`).
24
-
25
- ## Usage
26
-
27
- ### Front-end (Vue/React/etc.)
28
-
29
- ```ts
30
- import { createCemiarAuthClient } from "@cemiar/auth-sdk";
31
-
32
- const authClient = createCemiarAuthClient({
33
- baseUrl: import.meta.env.VITE_CEMIAR_AUTH_URL!,
34
- tenantId: import.meta.env.VITE_CEMIAR_TENANT_ID!,
35
- auds: (import.meta.env.VITE_CEMIAR_AUDS || "").split(","),
36
- redirectUrl: `${window.location.origin}/auth/microsoft/callback`,
37
- logoutRedirectUrl: `${window.location.origin}/login`
38
- });
39
-
40
- // Trigger Microsoft login
41
- window.location.href = authClient.getMicrosoftLoginUrl();
42
-
43
- // Email code flow
44
- await authClient.sendEmailCode("user@example.com");
45
- const { accessToken } = await authClient.verifyEmailCode("user@example.com", "123456");
46
-
47
- // Axios client with automatic Authorization header and refresh-on-401
48
- const api = authClient.createApiClient({ baseURL: import.meta.env.VITE_API_BASE });
49
- const jobs = await api.get("/api/jobs");
50
-
51
- // Manual interceptor attachment if you already have an axios instance
52
- authClient.attachInterceptors(existingAxiosInstance);
53
- ```
54
-
55
- #### Handling the Microsoft callback
56
-
57
- ```ts
58
- const params = Object.fromEntries(new URLSearchParams(window.location.search));
59
- const user = authClient.handleRedirectMicrosoftCallback(params);
60
-
61
- if (user) {
62
- history.replaceState(null, "", "/dashboard");
63
- }
64
- ```
65
-
66
- `handleRedirectMicrosoftCallback` stores the returned access token, infers the login method for future logouts, and gives you the signed-in user details. Call it once on your redirect page, then clean up the query string if needed.
67
-
68
- #### Token storage hooks
69
-
70
- By default the client stores tokens in `localStorage`. Override storage to customize behaviour:
71
-
72
- ```ts
73
- import { createCemiarAuthClient, TokenStorage } from "@cemiar/auth-sdk";
74
-
75
- const storage: TokenStorage = {
76
- getToken: () => cookies.get("cemiar-access"),
77
- setToken: token => {
78
- token ? cookies.set("cemiar-access", token) : cookies.remove("cemiar-access");
79
- }
80
- };
81
-
82
- const authClient = createCemiarAuthClient({
83
- baseUrl: import.meta.env.VITE_CEMIAR_AUTH_URL!,
84
- tenantId: import.meta.env.VITE_CEMIAR_TENANT_ID!,
85
- auds: ["cemiar-jobs"],
86
- storage
87
- });
88
- ```
89
-
90
- #### Logging out
91
-
92
- ```ts
93
- await authClient.logout();
94
- ```
95
-
96
- Logout automatically detects whether the user signed in with Microsoft or email and routes to the matching Cemiar Auth endpoint. Override behaviour with the optional flags:
97
-
98
- - `redirectUrl`: override the post-logout redirect (defaults to `logoutRedirectUrl` from the constructor)
99
- - `performRedirect`: set to `false` to stay on the page and only clear the server session
100
- - `clearToken`: set to `false` to retain the locally cached access token
101
-
102
- You can still specify `method: "microsoft" | "emailCode"` if you need to force a flow.
103
-
104
- ### Backend (Hapi)
105
-
106
- ```ts
107
- import { registerCemiarAuthHapi } from "@cemiar/auth-sdk";
108
-
109
- await registerCemiarAuthHapi(server, {
110
- authUrl: process.env.CEMIAR_AUTH_URL!,
111
- strategyName: "jwt",
112
- validate: async decoded => ({
113
- isValid: true,
114
- credentials: decoded.payload
115
- })
116
- });
117
- ```
118
-
119
- The helper automatically fetches signing keys from Cemiar Auth JWKS endpoint and keeps them cached.
120
-
121
- To customise the cache and algorithms:
122
-
123
- ```ts
124
- await registerCemiarAuthHapi(server, {
125
- authUrl: process.env.CEMIAR_AUTH_URL!,
126
- algorithms: ["RS256"],
127
- cacheMaxAgeMs: 12 * 60 * 60 * 1000
128
- });
129
- ```
130
-
131
- ### Environment Variables
132
-
133
- | Variable | Description |
134
- | -------------------------- | ------------------------------------------------------------------ |
135
- | `VITE_CEMIAR_AUTH_URL` | Base Cemiar Auth URL (e.g. `https://cemiar-auth.example.com/auth`) |
136
- | `VITE_CEMIAR_TENANT_ID` | Tenant identifier provided by Cemiar Auth |
137
- | `VITE_CEMIAR_AUDS` | Comma separated list of audiences |
138
- | `VITE_CEMIAR_REDIRECT_URL` | Optional override for Microsoft redirect |
139
- | `CEMIAR_AUTH_URL` | Backend URL for Cemiar Auth (no Vite prefix) |
140
-
141
- ### Building
142
-
143
- ```bash
144
- cd packages/cemiar-auth-sdk
145
- npm install
146
- npm run build
147
- ```
148
-
149
- Output will be generated under `dist/`.
1
+ # @cemiar/auth-sdk
2
+
3
+ Reusable helpers for integrating Cemiar Auth in web front-ends and Node.js APIs.
4
+
5
+ This package bundles:
6
+
7
+ - A browser-friendly Cemiar Auth client with email codes, Microsoft login support, token storage, refresh and logout helpers
8
+ - Axios utilities to attach automatic Authorization headers and refresh tokens on 401 responses
9
+ - A Hapi JWT strategy helper that fetches Cemiar Auth signing keys from JWKS and validates incoming requests
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install @cemiar/auth-sdk
15
+ ```
16
+
17
+ When working inside this monorepo, you can add it via a file reference:
18
+
19
+ ```bash
20
+ npm install @cemiar/auth-sdk@file:../packages/cemiar-auth-sdk
21
+ ```
22
+
23
+ Make sure the host project also has the required peer dependencies installed (`@hapi/hapi`, `hapi-auth-jwt2`).
24
+
25
+ ## Usage
26
+
27
+ ### Front-end (Vue/React/etc.)
28
+
29
+ ```ts
30
+ import { createCemiarAuthClient } from "@cemiar/auth-sdk";
31
+
32
+ const authClient = createCemiarAuthClient({
33
+ baseUrl: import.meta.env.VITE_CEMIAR_AUTH_URL!,
34
+ tenantId: import.meta.env.VITE_CEMIAR_TENANT_ID!,
35
+ auds: (import.meta.env.VITE_CEMIAR_AUDS || "").split(","),
36
+ redirectUrl: `${window.location.origin}/auth/microsoft/callback`,
37
+ logoutRedirectUrl: `${window.location.origin}/login`
38
+ });
39
+
40
+ // Trigger Microsoft login
41
+ window.location.href = authClient.getMicrosoftLoginUrl();
42
+
43
+ // Email code flow
44
+ await authClient.sendEmailCode("user@example.com");
45
+ const { accessToken } = await authClient.verifyEmailCode("user@example.com", "123456");
46
+
47
+ // Axios client with automatic Authorization header and refresh-on-401
48
+ const api = authClient.createApiClient({ baseURL: import.meta.env.VITE_API_BASE });
49
+ const jobs = await api.get("/api/jobs");
50
+
51
+ // Manual interceptor attachment if you already have an axios instance
52
+ authClient.attachInterceptors(existingAxiosInstance);
53
+ ```
54
+
55
+ #### Handling the Microsoft callback
56
+
57
+ ```ts
58
+ const params = Object.fromEntries(new URLSearchParams(window.location.search));
59
+ const user = authClient.handleRedirectMicrosoftCallback(params);
60
+
61
+ if (user) {
62
+ history.replaceState(null, "", "/dashboard");
63
+ }
64
+ ```
65
+
66
+ `handleRedirectMicrosoftCallback` stores the returned access token, infers the login method for future logouts, and gives you the signed-in user details. Call it once on your redirect page, then clean up the query string if needed.
67
+
68
+ #### Token storage hooks
69
+
70
+ By default the client stores tokens in `localStorage`. Override storage to customize behaviour:
71
+
72
+ ```ts
73
+ import { createCemiarAuthClient, TokenStorage } from "@cemiar/auth-sdk";
74
+
75
+ const storage: TokenStorage = {
76
+ getToken: () => cookies.get("cemiar-access"),
77
+ setToken: token => {
78
+ token ? cookies.set("cemiar-access", token) : cookies.remove("cemiar-access");
79
+ }
80
+ };
81
+
82
+ const authClient = createCemiarAuthClient({
83
+ baseUrl: import.meta.env.VITE_CEMIAR_AUTH_URL!,
84
+ tenantId: import.meta.env.VITE_CEMIAR_TENANT_ID!,
85
+ auds: ["cemiar-jobs"],
86
+ storage
87
+ });
88
+ ```
89
+
90
+ #### Logging out
91
+
92
+ ```ts
93
+ await authClient.logout();
94
+ ```
95
+
96
+ Logout automatically detects whether the user signed in with Microsoft or email and routes to the matching Cemiar Auth endpoint. Override behaviour with the optional flags:
97
+
98
+ - `redirectUrl`: override the post-logout redirect (defaults to `logoutRedirectUrl` from the constructor)
99
+ - `performRedirect`: set to `false` to stay on the page and only clear the server session
100
+ - `clearToken`: set to `false` to retain the locally cached access token
101
+
102
+ You can still specify `method: "microsoft" | "emailCode"` if you need to force a flow.
103
+
104
+ ### Backend (Hapi)
105
+
106
+ ```ts
107
+ import { registerCemiarAuthHapi } from "@cemiar/auth-sdk";
108
+
109
+ await registerCemiarAuthHapi(server, {
110
+ authUrl: process.env.CEMIAR_AUTH_URL!,
111
+ strategyName: "jwt",
112
+ validate: async decoded => ({
113
+ isValid: true,
114
+ credentials: decoded.payload
115
+ })
116
+ });
117
+ ```
118
+
119
+ The helper automatically fetches signing keys from Cemiar Auth JWKS endpoint and keeps them cached.
120
+
121
+ To customise the cache and algorithms:
122
+
123
+ ```ts
124
+ await registerCemiarAuthHapi(server, {
125
+ authUrl: process.env.CEMIAR_AUTH_URL!,
126
+ algorithms: ["RS256"],
127
+ cacheMaxAgeMs: 12 * 60 * 60 * 1000
128
+ });
129
+ ```
130
+
131
+ ### Environment Variables
132
+
133
+ | Variable | Description |
134
+ | -------------------------- | ------------------------------------------------------------------ |
135
+ | `VITE_CEMIAR_AUTH_URL` | Base Cemiar Auth URL (e.g. `https://cemiar-auth.example.com/auth`) |
136
+ | `VITE_CEMIAR_TENANT_ID` | Tenant identifier provided by Cemiar Auth |
137
+ | `VITE_CEMIAR_AUDS` | Comma separated list of audiences |
138
+ | `VITE_CEMIAR_REDIRECT_URL` | Optional override for Microsoft redirect |
139
+ | `CEMIAR_AUTH_URL` | Backend URL for Cemiar Auth (no Vite prefix) |
140
+
141
+ ### Building
142
+
143
+ ```bash
144
+ cd packages/cemiar-auth-sdk
145
+ npm install
146
+ npm run build
147
+ ```
148
+
149
+ Output will be generated under `dist/`.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { CemiarAuthClient, createCemiarAuthClient } from "./browser/CemiarAuthClient.js";
2
2
  export { registerCemiarAuthHapi } from "./server/ServerHandler.js";
3
3
  export * from "./shared/Types.js";
4
+ export { tokenToClaim, tokenToClaimBrowser, normalizeBaseUrl, isBrowser } from "./shared/Utils.js";
5
+ export { authorizeRoute } from "./server/AuthorizationHandler.js";
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export { CemiarAuthClient, createCemiarAuthClient } from "./browser/CemiarAuthClient.js";
2
2
  export { registerCemiarAuthHapi } from "./server/ServerHandler.js";
3
3
  export * from "./shared/Types.js";
4
+ export { tokenToClaim, tokenToClaimBrowser, normalizeBaseUrl, isBrowser } from "./shared/Utils.js";
5
+ export { authorizeRoute } from "./server/AuthorizationHandler.js";
@@ -0,0 +1,5 @@
1
+ import { ReqRefDefaults, ResponseToolkit } from "@hapi/hapi";
2
+ export declare function authorizeRoute({ resource, action }: {
3
+ resource: string;
4
+ action: string;
5
+ }): (request: any, h: ResponseToolkit<ReqRefDefaults>) => Promise<symbol | import("@hapi/hapi").ResponseObject>;
@@ -0,0 +1,52 @@
1
+ import axios from "axios";
2
+ import { tokenToClaim } from "../shared/Utils.js";
3
+ export function authorizeRoute({ resource, action }) {
4
+ return async (request, h) => {
5
+ const context = buildContextFromRequest(request);
6
+ if (!context) {
7
+ return h.response({ error: "Unauthorized: Invalid token or missing context" }).code(401).takeover();
8
+ }
9
+ const authzRequest = {
10
+ tenantId: context.tenantId,
11
+ resource,
12
+ action,
13
+ context
14
+ };
15
+ try {
16
+ const response = await axios.post(`${process.env.CEMIAR_AUTHZ_URL}/authorize`, authzRequest);
17
+ h;
18
+ if (response.status === 200 && response.data.allowed) {
19
+ h.request.headers["x-user-email"] = context.email || "";
20
+ return h.continue;
21
+ }
22
+ else {
23
+ return h.response({ error: "Forbidden: Access denied" }).code(403).takeover();
24
+ }
25
+ }
26
+ catch (error) {
27
+ return h
28
+ .response({ error: "Authorization service error: " + error.message })
29
+ .code(500)
30
+ .takeover();
31
+ }
32
+ };
33
+ }
34
+ function buildContextFromRequest(request) {
35
+ var _a, _b;
36
+ const token = (_b = (_a = request.headers) === null || _a === void 0 ? void 0 : _a.authorization) === null || _b === void 0 ? void 0 : _b.split(" ")[1];
37
+ if (!token) {
38
+ return null;
39
+ }
40
+ if (!token) {
41
+ return null;
42
+ }
43
+ const claim = tokenToClaim(token);
44
+ if (!claim) {
45
+ return null;
46
+ }
47
+ return {
48
+ email: claim.sub,
49
+ roles: [],
50
+ tenantId: process.env.CEMIAR_AUTH_TENANT_ID || ""
51
+ };
52
+ }
@@ -1,2 +1,3 @@
1
1
  export { registerCemiarAuthHapi, createSigningKeyResolver } from "./ServerHandler.js";
2
2
  export type { JwtValidateFn, JwtValidateResult, JwtParts, HapiJwtStrategyOptions } from "../shared/Types.js";
3
+ export { authorizeRoute } from "./AuthorizationHandler.js";
@@ -1 +1,2 @@
1
1
  export { registerCemiarAuthHapi, createSigningKeyResolver } from "./ServerHandler.js";
2
+ export { authorizeRoute } from "./AuthorizationHandler.js";
package/package.json CHANGED
@@ -1,68 +1,68 @@
1
- {
2
- "name": "@cemiar/auth-sdk",
3
- "version": "1.0.15",
4
- "description": "Cemiar Auth integration helpers for web apps and APIs.",
5
- "type": "module",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js",
12
- "require": "./dist/index.js"
13
- },
14
- "./server": {
15
- "types": "./dist/server/index.d.ts",
16
- "import": "./dist/server/index.js",
17
- "require": "./dist/server/index.js"
18
- },
19
- "./browser": {
20
- "types": "./dist/browser/CemiarAuthClient.d.ts",
21
- "import": "./dist/browser/CemiarAuthClient.js"
22
- }
23
- },
24
- "typesVersions": {
25
- "*": {
26
- "server": [
27
- "./dist/server/index.d.ts"
28
- ],
29
- "browser": [
30
- "./dist/browser/CemiarAuthClient.d.ts"
31
- ]
32
- }
33
- },
34
- "files": [
35
- "dist"
36
- ],
37
- "scripts": {
38
- "build": "tsc -p tsconfig.json",
39
- "clean": "rimraf dist"
40
- },
41
- "keywords": [
42
- "cemiar",
43
- "auth",
44
- "jwt",
45
- "hapi",
46
- "axios"
47
- ],
48
- "author": "Cemiar",
49
- "license": "MIT",
50
- "dependencies": {
51
- "axios": "^1.7.0",
52
- "jwks-rsa": "^3.1.0"
53
- },
54
- "peerDependencies": {
55
- "@hapi/hapi": "^21.3.0",
56
- "hapi-auth-jwt2": "^10.5.0"
57
- },
58
- "devDependencies": {
59
- "@hapi/hapi": "^21.3.0",
60
- "@types/node": "^20.11.30",
61
- "hapi-auth-jwt2": "^10.5.0",
62
- "rimraf": "^5.0.5",
63
- "typescript": "^5.3.3"
64
- },
65
- "publishConfig": {
66
- "access": "public"
67
- }
68
- }
1
+ {
2
+ "name": "@cemiar/auth-sdk",
3
+ "version": "1.0.17",
4
+ "description": "Cemiar Auth integration helpers for web apps and APIs.",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "./server": {
15
+ "types": "./dist/server/index.d.ts",
16
+ "import": "./dist/server/index.js",
17
+ "require": "./dist/server/index.js"
18
+ },
19
+ "./browser": {
20
+ "types": "./dist/browser/CemiarAuthClient.d.ts",
21
+ "import": "./dist/browser/CemiarAuthClient.js"
22
+ }
23
+ },
24
+ "typesVersions": {
25
+ "*": {
26
+ "server": [
27
+ "./dist/server/index.d.ts"
28
+ ],
29
+ "browser": [
30
+ "./dist/browser/CemiarAuthClient.d.ts"
31
+ ]
32
+ }
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc -p tsconfig.json",
39
+ "clean": "rimraf dist"
40
+ },
41
+ "keywords": [
42
+ "cemiar",
43
+ "auth",
44
+ "jwt",
45
+ "hapi",
46
+ "axios"
47
+ ],
48
+ "author": "Cemiar",
49
+ "license": "MIT",
50
+ "dependencies": {
51
+ "axios": "^1.7.0",
52
+ "jwks-rsa": "^3.1.0"
53
+ },
54
+ "peerDependencies": {
55
+ "@hapi/hapi": "^21.3.0",
56
+ "hapi-auth-jwt2": "^10.5.0"
57
+ },
58
+ "devDependencies": {
59
+ "@hapi/hapi": "^21.3.0",
60
+ "@types/node": "^20.11.30",
61
+ "hapi-auth-jwt2": "^10.5.0",
62
+ "rimraf": "^5.0.5",
63
+ "typescript": "^5.3.3"
64
+ },
65
+ "publishConfig": {
66
+ "access": "public"
67
+ }
68
+ }