@chemmangat/msal-next 2.0.1 → 2.1.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/README.md +568 -551
- package/dist/index.d.mts +1 -44
- package/dist/index.d.ts +1 -44
- package/dist/index.js +2 -1415
- package/dist/index.mjs +2 -1378
- package/dist/server.d.mts +0 -1
- package/dist/server.d.ts +0 -1
- package/dist/server.js +1 -91
- package/dist/server.mjs +1 -88
- package/package.json +94 -77
- package/SECURITY.md +0 -152
- package/dist/index.js.map +0 -1
- package/dist/index.mjs.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/server.mjs.map +0 -1
package/dist/server.d.mts
CHANGED
package/dist/server.d.ts
CHANGED
package/dist/server.js
CHANGED
|
@@ -1,91 +1 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var headers = require('next/headers');
|
|
4
|
-
|
|
5
|
-
// src/utils/getServerSession.ts
|
|
6
|
-
|
|
7
|
-
// src/utils/validation.ts
|
|
8
|
-
function safeJsonParse(jsonString, validator) {
|
|
9
|
-
try {
|
|
10
|
-
const parsed = JSON.parse(jsonString);
|
|
11
|
-
if (validator(parsed)) {
|
|
12
|
-
return parsed;
|
|
13
|
-
}
|
|
14
|
-
console.warn("[Validation] JSON validation failed");
|
|
15
|
-
return null;
|
|
16
|
-
} catch (error) {
|
|
17
|
-
console.error("[Validation] JSON parse error:", error);
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function isValidAccountData(data) {
|
|
22
|
-
return typeof data === "object" && data !== null && typeof data.homeAccountId === "string" && data.homeAccountId.length > 0 && typeof data.username === "string" && data.username.length > 0 && (data.name === void 0 || typeof data.name === "string");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// src/utils/getServerSession.ts
|
|
26
|
-
async function getServerSession() {
|
|
27
|
-
try {
|
|
28
|
-
const cookieStore = await headers.cookies();
|
|
29
|
-
const headersList = await headers.headers();
|
|
30
|
-
const msalAccount = cookieStore.get("msal.account");
|
|
31
|
-
const msalToken = cookieStore.get("msal.token");
|
|
32
|
-
if (msalAccount?.value) {
|
|
33
|
-
const accountData = safeJsonParse(
|
|
34
|
-
msalAccount.value,
|
|
35
|
-
isValidAccountData
|
|
36
|
-
);
|
|
37
|
-
if (accountData) {
|
|
38
|
-
return {
|
|
39
|
-
isAuthenticated: true,
|
|
40
|
-
accountId: accountData.homeAccountId,
|
|
41
|
-
username: accountData.username,
|
|
42
|
-
accessToken: msalToken?.value
|
|
43
|
-
};
|
|
44
|
-
} else {
|
|
45
|
-
console.warn("[ServerSession] Invalid account data in cookie");
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const authHeader = headersList.get("x-msal-authenticated");
|
|
49
|
-
if (authHeader === "true") {
|
|
50
|
-
const username = headersList.get("x-msal-username");
|
|
51
|
-
return {
|
|
52
|
-
isAuthenticated: true,
|
|
53
|
-
username: username || void 0
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
isAuthenticated: false
|
|
58
|
-
};
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error("[ServerSession] Error reading session:", error);
|
|
61
|
-
return {
|
|
62
|
-
isAuthenticated: false
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
async function setServerSessionCookie(account, accessToken) {
|
|
67
|
-
try {
|
|
68
|
-
const accountData = {
|
|
69
|
-
homeAccountId: account.homeAccountId,
|
|
70
|
-
username: account.username,
|
|
71
|
-
name: account.name
|
|
72
|
-
};
|
|
73
|
-
await fetch("/api/auth/session", {
|
|
74
|
-
method: "POST",
|
|
75
|
-
headers: {
|
|
76
|
-
"Content-Type": "application/json"
|
|
77
|
-
},
|
|
78
|
-
body: JSON.stringify({
|
|
79
|
-
account: accountData,
|
|
80
|
-
token: accessToken
|
|
81
|
-
})
|
|
82
|
-
});
|
|
83
|
-
} catch (error) {
|
|
84
|
-
console.error("[ServerSession] Failed to set session cookie:", error);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
exports.getServerSession = getServerSession;
|
|
89
|
-
exports.setServerSessionCookie = setServerSessionCookie;
|
|
90
|
-
//# sourceMappingURL=server.js.map
|
|
91
|
-
//# sourceMappingURL=server.js.map
|
|
1
|
+
'use strict';var headers=require('next/headers');async function i(){try{let e=await headers.cookies(),r=await headers.headers(),t=e.get("msal.account"),o=e.get("msal.token");if(t?.value)try{let s=JSON.parse(t.value);return {isAuthenticated:!0,accountId:s.homeAccountId,username:s.username,accessToken:o?.value}}catch(s){console.error("[ServerSession] Failed to parse account data:",s);}return r.get("x-msal-authenticated")==="true"?{isAuthenticated:!0,username:r.get("x-msal-username")||void 0}:{isAuthenticated:!1}}catch(e){return console.error("[ServerSession] Error reading session:",e),{isAuthenticated:false}}}async function c(e,r){try{let t={homeAccountId:e.homeAccountId,username:e.username,name:e.name};await fetch("/api/auth/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({account:t,token:r})});}catch(t){console.error("[ServerSession] Failed to set session cookie:",t);}}exports.getServerSession=i;exports.setServerSessionCookie=c;
|
package/dist/server.mjs
CHANGED
|
@@ -1,88 +1 @@
|
|
|
1
|
-
import { cookies, headers }
|
|
2
|
-
|
|
3
|
-
// src/utils/getServerSession.ts
|
|
4
|
-
|
|
5
|
-
// src/utils/validation.ts
|
|
6
|
-
function safeJsonParse(jsonString, validator) {
|
|
7
|
-
try {
|
|
8
|
-
const parsed = JSON.parse(jsonString);
|
|
9
|
-
if (validator(parsed)) {
|
|
10
|
-
return parsed;
|
|
11
|
-
}
|
|
12
|
-
console.warn("[Validation] JSON validation failed");
|
|
13
|
-
return null;
|
|
14
|
-
} catch (error) {
|
|
15
|
-
console.error("[Validation] JSON parse error:", error);
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
function isValidAccountData(data) {
|
|
20
|
-
return typeof data === "object" && data !== null && typeof data.homeAccountId === "string" && data.homeAccountId.length > 0 && typeof data.username === "string" && data.username.length > 0 && (data.name === void 0 || typeof data.name === "string");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// src/utils/getServerSession.ts
|
|
24
|
-
async function getServerSession() {
|
|
25
|
-
try {
|
|
26
|
-
const cookieStore = await cookies();
|
|
27
|
-
const headersList = await headers();
|
|
28
|
-
const msalAccount = cookieStore.get("msal.account");
|
|
29
|
-
const msalToken = cookieStore.get("msal.token");
|
|
30
|
-
if (msalAccount?.value) {
|
|
31
|
-
const accountData = safeJsonParse(
|
|
32
|
-
msalAccount.value,
|
|
33
|
-
isValidAccountData
|
|
34
|
-
);
|
|
35
|
-
if (accountData) {
|
|
36
|
-
return {
|
|
37
|
-
isAuthenticated: true,
|
|
38
|
-
accountId: accountData.homeAccountId,
|
|
39
|
-
username: accountData.username,
|
|
40
|
-
accessToken: msalToken?.value
|
|
41
|
-
};
|
|
42
|
-
} else {
|
|
43
|
-
console.warn("[ServerSession] Invalid account data in cookie");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const authHeader = headersList.get("x-msal-authenticated");
|
|
47
|
-
if (authHeader === "true") {
|
|
48
|
-
const username = headersList.get("x-msal-username");
|
|
49
|
-
return {
|
|
50
|
-
isAuthenticated: true,
|
|
51
|
-
username: username || void 0
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
return {
|
|
55
|
-
isAuthenticated: false
|
|
56
|
-
};
|
|
57
|
-
} catch (error) {
|
|
58
|
-
console.error("[ServerSession] Error reading session:", error);
|
|
59
|
-
return {
|
|
60
|
-
isAuthenticated: false
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
async function setServerSessionCookie(account, accessToken) {
|
|
65
|
-
try {
|
|
66
|
-
const accountData = {
|
|
67
|
-
homeAccountId: account.homeAccountId,
|
|
68
|
-
username: account.username,
|
|
69
|
-
name: account.name
|
|
70
|
-
};
|
|
71
|
-
await fetch("/api/auth/session", {
|
|
72
|
-
method: "POST",
|
|
73
|
-
headers: {
|
|
74
|
-
"Content-Type": "application/json"
|
|
75
|
-
},
|
|
76
|
-
body: JSON.stringify({
|
|
77
|
-
account: accountData,
|
|
78
|
-
token: accessToken
|
|
79
|
-
})
|
|
80
|
-
});
|
|
81
|
-
} catch (error) {
|
|
82
|
-
console.error("[ServerSession] Failed to set session cookie:", error);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export { getServerSession, setServerSessionCookie };
|
|
87
|
-
//# sourceMappingURL=server.mjs.map
|
|
88
|
-
//# sourceMappingURL=server.mjs.map
|
|
1
|
+
import {cookies,headers}from'next/headers';async function i(){try{let e=await cookies(),r=await headers(),t=e.get("msal.account"),o=e.get("msal.token");if(t?.value)try{let s=JSON.parse(t.value);return {isAuthenticated:!0,accountId:s.homeAccountId,username:s.username,accessToken:o?.value}}catch(s){console.error("[ServerSession] Failed to parse account data:",s);}return r.get("x-msal-authenticated")==="true"?{isAuthenticated:!0,username:r.get("x-msal-username")||void 0}:{isAuthenticated:!1}}catch(e){return console.error("[ServerSession] Error reading session:",e),{isAuthenticated:false}}}async function c(e,r){try{let t={homeAccountId:e.homeAccountId,username:e.username,name:e.name};await fetch("/api/auth/session",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({account:t,token:r})});}catch(t){console.error("[ServerSession] Failed to set session cookie:",t);}}export{i as getServerSession,c as setServerSessionCookie};
|
package/package.json
CHANGED
|
@@ -1,77 +1,94 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@chemmangat/msal-next",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Production-grade MSAL authentication package for Next.js App Router with minimal boilerplate",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"module": "./dist/index.mjs",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.mjs",
|
|
12
|
-
"require": "./dist/index.js"
|
|
13
|
-
},
|
|
14
|
-
"./server": {
|
|
15
|
-
"types": "./dist/server.d.ts",
|
|
16
|
-
"import": "./dist/server.mjs",
|
|
17
|
-
"require": "./dist/server.js"
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist",
|
|
22
|
-
"README.md"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"test": "vitest
|
|
29
|
-
"test:
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"directory"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"
|
|
65
|
-
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"
|
|
76
|
-
|
|
77
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@chemmangat/msal-next",
|
|
3
|
+
"version": "2.1.1",
|
|
4
|
+
"description": "Production-grade MSAL authentication package for Next.js App Router with minimal boilerplate",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./server": {
|
|
15
|
+
"types": "./dist/server.d.ts",
|
|
16
|
+
"import": "./dist/server.mjs",
|
|
17
|
+
"require": "./dist/server.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"dev": "tsup --watch",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"test:coverage": "vitest run --coverage",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"msal",
|
|
34
|
+
"nextjs",
|
|
35
|
+
"next.js",
|
|
36
|
+
"next",
|
|
37
|
+
"authentication",
|
|
38
|
+
"auth",
|
|
39
|
+
"azure-ad",
|
|
40
|
+
"azure",
|
|
41
|
+
"microsoft",
|
|
42
|
+
"microsoft-authentication",
|
|
43
|
+
"oauth",
|
|
44
|
+
"oauth2",
|
|
45
|
+
"app-router",
|
|
46
|
+
"typescript",
|
|
47
|
+
"sso",
|
|
48
|
+
"single-sign-on",
|
|
49
|
+
"microsoft-graph",
|
|
50
|
+
"graph-api",
|
|
51
|
+
"azure-active-directory",
|
|
52
|
+
"entra-id",
|
|
53
|
+
"login",
|
|
54
|
+
"signin",
|
|
55
|
+
"react",
|
|
56
|
+
"msal-react",
|
|
57
|
+
"msal-browser",
|
|
58
|
+
"next14",
|
|
59
|
+
"next15",
|
|
60
|
+
"nextjs-auth",
|
|
61
|
+
"microsoft-login"
|
|
62
|
+
],
|
|
63
|
+
"author": "Chemmangat",
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "git",
|
|
67
|
+
"url": "https://github.com/chemmangat/msal-next.git",
|
|
68
|
+
"directory": "packages/core"
|
|
69
|
+
},
|
|
70
|
+
"homepage": "https://github.com/chemmangat/msal-next#readme",
|
|
71
|
+
"bugs": {
|
|
72
|
+
"url": "https://github.com/chemmangat/msal-next/issues"
|
|
73
|
+
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"@azure/msal-browser": "^3.11.0 || ^4.0.0",
|
|
76
|
+
"@azure/msal-react": "^2.0.0 || ^3.0.0",
|
|
77
|
+
"next": ">=14.0.0",
|
|
78
|
+
"react": ">=18.0.0",
|
|
79
|
+
"react-dom": ">=18.0.0"
|
|
80
|
+
},
|
|
81
|
+
"devDependencies": {
|
|
82
|
+
"@azure/msal-browser": "^3.11.1",
|
|
83
|
+
"@azure/msal-react": "^2.0.15",
|
|
84
|
+
"@testing-library/react": "^14.0.0",
|
|
85
|
+
"@testing-library/react-hooks": "^8.0.1",
|
|
86
|
+
"@types/react": "^18.2.0",
|
|
87
|
+
"@vitest/coverage-v8": "^1.0.0",
|
|
88
|
+
"jsdom": "^23.0.0",
|
|
89
|
+
"react": "^18.2.0",
|
|
90
|
+
"tsup": "^8.0.1",
|
|
91
|
+
"typescript": "^5.3.0",
|
|
92
|
+
"vitest": "^1.0.0"
|
|
93
|
+
}
|
|
94
|
+
}
|
package/SECURITY.md
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
# Security Policy
|
|
2
|
-
|
|
3
|
-
## Supported Versions
|
|
4
|
-
|
|
5
|
-
| Version | Supported |
|
|
6
|
-
| ------- | ------------------ |
|
|
7
|
-
| 2.0.x | :white_check_mark: |
|
|
8
|
-
| < 2.0 | :x: |
|
|
9
|
-
|
|
10
|
-
## Security Updates in v2.0.1
|
|
11
|
-
|
|
12
|
-
This release addresses several security vulnerabilities discovered in v2.0.0. We strongly recommend all users upgrade immediately.
|
|
13
|
-
|
|
14
|
-
### Fixed Vulnerabilities
|
|
15
|
-
|
|
16
|
-
#### 1. Memory Leaks from Blob URLs (Medium Severity)
|
|
17
|
-
**Affected:** `useUserProfile` hook
|
|
18
|
-
**Fixed:** Added proper cleanup of blob URLs using `URL.revokeObjectURL()` in useEffect cleanup functions.
|
|
19
|
-
|
|
20
|
-
#### 2. Unbounded Cache Growth (Medium Severity)
|
|
21
|
-
**Affected:** `useRoles` and `useUserProfile` hooks
|
|
22
|
-
**Fixed:** Implemented LRU cache eviction with a maximum size limit of 100 entries to prevent memory exhaustion.
|
|
23
|
-
|
|
24
|
-
#### 3. Race Conditions in Token Acquisition (Medium Severity)
|
|
25
|
-
**Affected:** `useMsalAuth` hook
|
|
26
|
-
**Fixed:** Implemented request deduplication to prevent multiple concurrent popup windows and token requests.
|
|
27
|
-
|
|
28
|
-
#### 4. JSON Parsing Without Validation (High Severity)
|
|
29
|
-
**Affected:** `getServerSession`, `createAuthMiddleware`
|
|
30
|
-
**Fixed:** Added schema validation for all JSON parsing operations using the new `safeJsonParse` utility.
|
|
31
|
-
|
|
32
|
-
#### 5. Information Disclosure in Error Messages (Medium Severity)
|
|
33
|
-
**Affected:** All hooks and utilities
|
|
34
|
-
**Fixed:** Implemented error sanitization to remove tokens, secrets, and sensitive information from error messages.
|
|
35
|
-
|
|
36
|
-
#### 6. Missing Redirect URI Validation (Medium Severity)
|
|
37
|
-
**Affected:** `createMsalConfig`
|
|
38
|
-
**Fixed:** Added optional `allowedRedirectUris` configuration to validate redirect URIs and prevent open redirect vulnerabilities.
|
|
39
|
-
|
|
40
|
-
### New Security Features
|
|
41
|
-
|
|
42
|
-
- **Input Validation Utilities**: New `validation.ts` module with functions for safe JSON parsing, scope validation, and redirect URI validation
|
|
43
|
-
- **Error Sanitization**: Automatic removal of tokens and secrets from error messages
|
|
44
|
-
- **Cache Management**: Proper cache size limits and cleanup on component unmount
|
|
45
|
-
- **Request Deduplication**: Prevention of concurrent token acquisition requests
|
|
46
|
-
|
|
47
|
-
## Best Practices
|
|
48
|
-
|
|
49
|
-
### DO NOT Store Tokens in Cookies
|
|
50
|
-
|
|
51
|
-
The example code in `src/examples/api-route-session.ts` demonstrates cookie-based session management but includes a warning. **Do not use this pattern in production** as it exposes tokens to CSRF attacks.
|
|
52
|
-
|
|
53
|
-
Instead:
|
|
54
|
-
- Use MSAL's built-in sessionStorage/localStorage (client-side only)
|
|
55
|
-
- Implement proper server-side session management with encrypted session IDs
|
|
56
|
-
- Never store access tokens in cookies
|
|
57
|
-
|
|
58
|
-
### Use Redirect URI Validation
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
<MsalAuthProvider
|
|
62
|
-
clientId={process.env.NEXT_PUBLIC_CLIENT_ID!}
|
|
63
|
-
allowedRedirectUris={[
|
|
64
|
-
'https://myapp.com',
|
|
65
|
-
'https://staging.myapp.com',
|
|
66
|
-
'http://localhost:3000'
|
|
67
|
-
]}
|
|
68
|
-
>
|
|
69
|
-
{children}
|
|
70
|
-
</MsalAuthProvider>
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Implement Security Headers
|
|
74
|
-
|
|
75
|
-
Add comprehensive security headers in your `next.config.js`:
|
|
76
|
-
|
|
77
|
-
```javascript
|
|
78
|
-
async headers() {
|
|
79
|
-
return [
|
|
80
|
-
{
|
|
81
|
-
source: '/(.*)',
|
|
82
|
-
headers: [
|
|
83
|
-
{
|
|
84
|
-
key: 'Content-Security-Policy',
|
|
85
|
-
value: "default-src 'self'; script-src 'self'; connect-src 'self' https://login.microsoftonline.com https://graph.microsoft.com;"
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
key: 'X-Frame-Options',
|
|
89
|
-
value: 'DENY'
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
key: 'X-Content-Type-Options',
|
|
93
|
-
value: 'nosniff'
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
key: 'Referrer-Policy',
|
|
97
|
-
value: 'strict-origin-when-cross-origin'
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
key: 'Permissions-Policy',
|
|
101
|
-
value: 'camera=(), microphone=(), geolocation=()'
|
|
102
|
-
}
|
|
103
|
-
]
|
|
104
|
-
}
|
|
105
|
-
];
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Validate Scopes
|
|
110
|
-
|
|
111
|
-
Use the built-in scope validation:
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
import { validateScopes } from '@chemmangat/msal-next';
|
|
115
|
-
|
|
116
|
-
const scopes = ['User.Read', 'Mail.Read'];
|
|
117
|
-
if (!validateScopes(scopes)) {
|
|
118
|
-
throw new Error('Invalid scopes');
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Use HTTPS in Development
|
|
123
|
-
|
|
124
|
-
Always test with HTTPS locally to catch security issues early:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
# Use mkcert or similar tools
|
|
128
|
-
mkcert localhost
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
## Reporting a Vulnerability
|
|
132
|
-
|
|
133
|
-
If you discover a security vulnerability, please email security@chemmangat.com with:
|
|
134
|
-
|
|
135
|
-
1. Description of the vulnerability
|
|
136
|
-
2. Steps to reproduce
|
|
137
|
-
3. Potential impact
|
|
138
|
-
4. Suggested fix (if any)
|
|
139
|
-
|
|
140
|
-
We will respond within 48 hours and provide a timeline for a fix.
|
|
141
|
-
|
|
142
|
-
## Security Checklist
|
|
143
|
-
|
|
144
|
-
- [ ] Updated to v2.0.1 or later
|
|
145
|
-
- [ ] Not storing tokens in cookies
|
|
146
|
-
- [ ] Security headers configured in next.config.js
|
|
147
|
-
- [ ] Redirect URI validation enabled
|
|
148
|
-
- [ ] Using HTTPS in production
|
|
149
|
-
- [ ] Regular dependency audits (`npm audit`)
|
|
150
|
-
- [ ] Environment variables properly secured
|
|
151
|
-
- [ ] CSRF protection on state-changing endpoints
|
|
152
|
-
- [ ] Rate limiting on authentication endpoints
|