@chemmangat/msal-next 1.0.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/README.md +104 -0
- package/dist/index.d.mts +126 -0
- package/dist/index.d.ts +126 -0
- package/dist/index.js +303 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +273 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# @chemmangat/msal-next
|
|
2
|
+
|
|
3
|
+
Fully configurable MSAL (Microsoft Authentication Library) package for Next.js App Router with TypeScript support.
|
|
4
|
+
|
|
5
|
+
## 🚀 Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @chemmangat/msal-next @azure/msal-browser @azure/msal-react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
// app/layout.tsx
|
|
13
|
+
import { MsalAuthProvider } from '@chemmangat/msal-next';
|
|
14
|
+
|
|
15
|
+
export default function RootLayout({ children }) {
|
|
16
|
+
return (
|
|
17
|
+
<html>
|
|
18
|
+
<body>
|
|
19
|
+
<MsalAuthProvider clientId="your-client-id">
|
|
20
|
+
{children}
|
|
21
|
+
</MsalAuthProvider>
|
|
22
|
+
</body>
|
|
23
|
+
</html>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// app/page.tsx
|
|
30
|
+
'use client';
|
|
31
|
+
import { useMsalAuth } from '@chemmangat/msal-next';
|
|
32
|
+
|
|
33
|
+
export default function Home() {
|
|
34
|
+
const { isAuthenticated, loginPopup } = useMsalAuth();
|
|
35
|
+
|
|
36
|
+
if (!isAuthenticated) {
|
|
37
|
+
return <button onClick={() => loginPopup()}>Sign In</button>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return <div>Welcome!</div>;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 📚 Documentation
|
|
45
|
+
|
|
46
|
+
Visit [https://msal-next.chemmangat.dev](https://msal-next.chemmangat.dev) for full documentation.
|
|
47
|
+
|
|
48
|
+
## ✨ Features
|
|
49
|
+
|
|
50
|
+
- ✅ Next.js 14+ App Router support
|
|
51
|
+
- ✅ TypeScript with full type definitions
|
|
52
|
+
- ✅ Multi-tenant and single-tenant authentication
|
|
53
|
+
- ✅ Popup and redirect authentication flows
|
|
54
|
+
- ✅ Automatic token acquisition with silent refresh
|
|
55
|
+
- ✅ Zero configuration for simple use cases
|
|
56
|
+
- ✅ Highly configurable when needed
|
|
57
|
+
|
|
58
|
+
## 📖 API
|
|
59
|
+
|
|
60
|
+
### MsalAuthProvider
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
<MsalAuthProvider
|
|
64
|
+
clientId="required"
|
|
65
|
+
tenantId="optional"
|
|
66
|
+
authorityType="common" // 'common' | 'organizations' | 'consumers' | 'tenant'
|
|
67
|
+
scopes={['User.Read']}
|
|
68
|
+
cacheLocation="sessionStorage"
|
|
69
|
+
enableLogging={false}
|
|
70
|
+
loadingComponent={<div>Loading...</div>}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</MsalAuthProvider>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### useMsalAuth Hook
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
const {
|
|
80
|
+
isAuthenticated,
|
|
81
|
+
account,
|
|
82
|
+
accounts,
|
|
83
|
+
inProgress,
|
|
84
|
+
loginPopup,
|
|
85
|
+
loginRedirect,
|
|
86
|
+
logoutPopup,
|
|
87
|
+
logoutRedirect,
|
|
88
|
+
acquireToken,
|
|
89
|
+
acquireTokenSilent,
|
|
90
|
+
acquireTokenPopup,
|
|
91
|
+
acquireTokenRedirect,
|
|
92
|
+
} = useMsalAuth();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 🔗 Links
|
|
96
|
+
|
|
97
|
+
- [Documentation](https://msal-next.chemmangat.dev)
|
|
98
|
+
- [GitHub](https://github.com/chemmangat/msal-next)
|
|
99
|
+
- [npm](https://www.npmjs.com/package/@chemmangat/msal-next)
|
|
100
|
+
- [Examples](https://github.com/chemmangat/msal-next/tree/main/example)
|
|
101
|
+
|
|
102
|
+
## 📄 License
|
|
103
|
+
|
|
104
|
+
MIT © Chemmangat
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { Configuration, LogLevel, AccountInfo } from '@azure/msal-browser';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
export { useAccount, useIsAuthenticated, useMsal } from '@azure/msal-react';
|
|
5
|
+
|
|
6
|
+
interface MsalAuthConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Azure AD Application (client) ID
|
|
9
|
+
*/
|
|
10
|
+
clientId: string;
|
|
11
|
+
/**
|
|
12
|
+
* Azure AD Directory (tenant) ID (optional for multi-tenant)
|
|
13
|
+
*/
|
|
14
|
+
tenantId?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Authority type: 'common' for multi-tenant, 'organizations', 'consumers', or 'tenant' for single-tenant
|
|
17
|
+
* @default 'common'
|
|
18
|
+
*/
|
|
19
|
+
authorityType?: 'common' | 'organizations' | 'consumers' | 'tenant';
|
|
20
|
+
/**
|
|
21
|
+
* Redirect URI after authentication
|
|
22
|
+
* @default window.location.origin
|
|
23
|
+
*/
|
|
24
|
+
redirectUri?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Post logout redirect URI
|
|
27
|
+
* @default redirectUri
|
|
28
|
+
*/
|
|
29
|
+
postLogoutRedirectUri?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Default scopes for authentication
|
|
32
|
+
* @default ['User.Read']
|
|
33
|
+
*/
|
|
34
|
+
scopes?: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Cache location: 'sessionStorage', 'localStorage', or 'memoryStorage'
|
|
37
|
+
* @default 'sessionStorage'
|
|
38
|
+
*/
|
|
39
|
+
cacheLocation?: 'sessionStorage' | 'localStorage' | 'memoryStorage';
|
|
40
|
+
/**
|
|
41
|
+
* Store auth state in cookie (for IE11/Edge legacy)
|
|
42
|
+
* @default false
|
|
43
|
+
*/
|
|
44
|
+
storeAuthStateInCookie?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Navigate to login request URL after authentication
|
|
47
|
+
* @default true
|
|
48
|
+
*/
|
|
49
|
+
navigateToLoginRequestUrl?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Custom MSAL configuration (overrides all other options)
|
|
52
|
+
*/
|
|
53
|
+
msalConfig?: Configuration;
|
|
54
|
+
/**
|
|
55
|
+
* Enable debug logging
|
|
56
|
+
* @default false
|
|
57
|
+
*/
|
|
58
|
+
enableLogging?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Custom logger callback
|
|
61
|
+
*/
|
|
62
|
+
loggerCallback?: (level: LogLevel, message: string, containsPii: boolean) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Loading component to show while MSAL initializes
|
|
65
|
+
*/
|
|
66
|
+
loadingComponent?: ReactNode;
|
|
67
|
+
}
|
|
68
|
+
interface MsalAuthProviderProps extends MsalAuthConfig {
|
|
69
|
+
children: ReactNode;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function MsalAuthProvider({ children, loadingComponent, ...config }: MsalAuthProviderProps): react_jsx_runtime.JSX.Element;
|
|
73
|
+
|
|
74
|
+
interface UseMsalAuthReturn {
|
|
75
|
+
/**
|
|
76
|
+
* Current authenticated account
|
|
77
|
+
*/
|
|
78
|
+
account: AccountInfo | null;
|
|
79
|
+
/**
|
|
80
|
+
* All accounts in the cache
|
|
81
|
+
*/
|
|
82
|
+
accounts: AccountInfo[];
|
|
83
|
+
/**
|
|
84
|
+
* Whether user is authenticated
|
|
85
|
+
*/
|
|
86
|
+
isAuthenticated: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Whether MSAL is currently performing an interaction
|
|
89
|
+
*/
|
|
90
|
+
inProgress: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Login using popup
|
|
93
|
+
*/
|
|
94
|
+
loginPopup: (scopes?: string[]) => Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Login using redirect
|
|
97
|
+
*/
|
|
98
|
+
loginRedirect: (scopes?: string[]) => Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Logout using popup
|
|
101
|
+
*/
|
|
102
|
+
logoutPopup: () => Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Logout using redirect
|
|
105
|
+
*/
|
|
106
|
+
logoutRedirect: () => Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Acquire access token silently (with fallback to popup)
|
|
109
|
+
*/
|
|
110
|
+
acquireToken: (scopes: string[]) => Promise<string>;
|
|
111
|
+
/**
|
|
112
|
+
* Acquire access token silently only (no fallback)
|
|
113
|
+
*/
|
|
114
|
+
acquireTokenSilent: (scopes: string[]) => Promise<string>;
|
|
115
|
+
/**
|
|
116
|
+
* Acquire access token using popup
|
|
117
|
+
*/
|
|
118
|
+
acquireTokenPopup: (scopes: string[]) => Promise<string>;
|
|
119
|
+
/**
|
|
120
|
+
* Acquire access token using redirect
|
|
121
|
+
*/
|
|
122
|
+
acquireTokenRedirect: (scopes: string[]) => Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
declare function useMsalAuth(defaultScopes?: string[]): UseMsalAuthReturn;
|
|
125
|
+
|
|
126
|
+
export { type MsalAuthConfig, MsalAuthProvider, type MsalAuthProviderProps, useMsalAuth };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { Configuration, LogLevel, AccountInfo } from '@azure/msal-browser';
|
|
3
|
+
import { ReactNode } from 'react';
|
|
4
|
+
export { useAccount, useIsAuthenticated, useMsal } from '@azure/msal-react';
|
|
5
|
+
|
|
6
|
+
interface MsalAuthConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Azure AD Application (client) ID
|
|
9
|
+
*/
|
|
10
|
+
clientId: string;
|
|
11
|
+
/**
|
|
12
|
+
* Azure AD Directory (tenant) ID (optional for multi-tenant)
|
|
13
|
+
*/
|
|
14
|
+
tenantId?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Authority type: 'common' for multi-tenant, 'organizations', 'consumers', or 'tenant' for single-tenant
|
|
17
|
+
* @default 'common'
|
|
18
|
+
*/
|
|
19
|
+
authorityType?: 'common' | 'organizations' | 'consumers' | 'tenant';
|
|
20
|
+
/**
|
|
21
|
+
* Redirect URI after authentication
|
|
22
|
+
* @default window.location.origin
|
|
23
|
+
*/
|
|
24
|
+
redirectUri?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Post logout redirect URI
|
|
27
|
+
* @default redirectUri
|
|
28
|
+
*/
|
|
29
|
+
postLogoutRedirectUri?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Default scopes for authentication
|
|
32
|
+
* @default ['User.Read']
|
|
33
|
+
*/
|
|
34
|
+
scopes?: string[];
|
|
35
|
+
/**
|
|
36
|
+
* Cache location: 'sessionStorage', 'localStorage', or 'memoryStorage'
|
|
37
|
+
* @default 'sessionStorage'
|
|
38
|
+
*/
|
|
39
|
+
cacheLocation?: 'sessionStorage' | 'localStorage' | 'memoryStorage';
|
|
40
|
+
/**
|
|
41
|
+
* Store auth state in cookie (for IE11/Edge legacy)
|
|
42
|
+
* @default false
|
|
43
|
+
*/
|
|
44
|
+
storeAuthStateInCookie?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Navigate to login request URL after authentication
|
|
47
|
+
* @default true
|
|
48
|
+
*/
|
|
49
|
+
navigateToLoginRequestUrl?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Custom MSAL configuration (overrides all other options)
|
|
52
|
+
*/
|
|
53
|
+
msalConfig?: Configuration;
|
|
54
|
+
/**
|
|
55
|
+
* Enable debug logging
|
|
56
|
+
* @default false
|
|
57
|
+
*/
|
|
58
|
+
enableLogging?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Custom logger callback
|
|
61
|
+
*/
|
|
62
|
+
loggerCallback?: (level: LogLevel, message: string, containsPii: boolean) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Loading component to show while MSAL initializes
|
|
65
|
+
*/
|
|
66
|
+
loadingComponent?: ReactNode;
|
|
67
|
+
}
|
|
68
|
+
interface MsalAuthProviderProps extends MsalAuthConfig {
|
|
69
|
+
children: ReactNode;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare function MsalAuthProvider({ children, loadingComponent, ...config }: MsalAuthProviderProps): react_jsx_runtime.JSX.Element;
|
|
73
|
+
|
|
74
|
+
interface UseMsalAuthReturn {
|
|
75
|
+
/**
|
|
76
|
+
* Current authenticated account
|
|
77
|
+
*/
|
|
78
|
+
account: AccountInfo | null;
|
|
79
|
+
/**
|
|
80
|
+
* All accounts in the cache
|
|
81
|
+
*/
|
|
82
|
+
accounts: AccountInfo[];
|
|
83
|
+
/**
|
|
84
|
+
* Whether user is authenticated
|
|
85
|
+
*/
|
|
86
|
+
isAuthenticated: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Whether MSAL is currently performing an interaction
|
|
89
|
+
*/
|
|
90
|
+
inProgress: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Login using popup
|
|
93
|
+
*/
|
|
94
|
+
loginPopup: (scopes?: string[]) => Promise<void>;
|
|
95
|
+
/**
|
|
96
|
+
* Login using redirect
|
|
97
|
+
*/
|
|
98
|
+
loginRedirect: (scopes?: string[]) => Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Logout using popup
|
|
101
|
+
*/
|
|
102
|
+
logoutPopup: () => Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Logout using redirect
|
|
105
|
+
*/
|
|
106
|
+
logoutRedirect: () => Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Acquire access token silently (with fallback to popup)
|
|
109
|
+
*/
|
|
110
|
+
acquireToken: (scopes: string[]) => Promise<string>;
|
|
111
|
+
/**
|
|
112
|
+
* Acquire access token silently only (no fallback)
|
|
113
|
+
*/
|
|
114
|
+
acquireTokenSilent: (scopes: string[]) => Promise<string>;
|
|
115
|
+
/**
|
|
116
|
+
* Acquire access token using popup
|
|
117
|
+
*/
|
|
118
|
+
acquireTokenPopup: (scopes: string[]) => Promise<string>;
|
|
119
|
+
/**
|
|
120
|
+
* Acquire access token using redirect
|
|
121
|
+
*/
|
|
122
|
+
acquireTokenRedirect: (scopes: string[]) => Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
declare function useMsalAuth(defaultScopes?: string[]): UseMsalAuthReturn;
|
|
125
|
+
|
|
126
|
+
export { type MsalAuthConfig, MsalAuthProvider, type MsalAuthProviderProps, useMsalAuth };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
MsalAuthProvider: () => MsalAuthProvider,
|
|
25
|
+
useAccount: () => import_msal_react3.useAccount,
|
|
26
|
+
useIsAuthenticated: () => import_msal_react3.useIsAuthenticated,
|
|
27
|
+
useMsal: () => import_msal_react3.useMsal,
|
|
28
|
+
useMsalAuth: () => useMsalAuth
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(index_exports);
|
|
31
|
+
|
|
32
|
+
// src/components/MsalAuthProvider.tsx
|
|
33
|
+
var import_msal_react = require("@azure/msal-react");
|
|
34
|
+
var import_msal_browser2 = require("@azure/msal-browser");
|
|
35
|
+
var import_react = require("react");
|
|
36
|
+
|
|
37
|
+
// src/utils/createMsalConfig.ts
|
|
38
|
+
var import_msal_browser = require("@azure/msal-browser");
|
|
39
|
+
function createMsalConfig(config) {
|
|
40
|
+
if (config.msalConfig) {
|
|
41
|
+
return config.msalConfig;
|
|
42
|
+
}
|
|
43
|
+
const {
|
|
44
|
+
clientId,
|
|
45
|
+
tenantId,
|
|
46
|
+
authorityType = "common",
|
|
47
|
+
redirectUri,
|
|
48
|
+
postLogoutRedirectUri,
|
|
49
|
+
cacheLocation = "sessionStorage",
|
|
50
|
+
storeAuthStateInCookie = false,
|
|
51
|
+
navigateToLoginRequestUrl = true,
|
|
52
|
+
enableLogging = false,
|
|
53
|
+
loggerCallback
|
|
54
|
+
} = config;
|
|
55
|
+
if (!clientId) {
|
|
56
|
+
throw new Error("@chemmangat/msal-next: clientId is required");
|
|
57
|
+
}
|
|
58
|
+
const getAuthority = () => {
|
|
59
|
+
if (authorityType === "tenant") {
|
|
60
|
+
if (!tenantId) {
|
|
61
|
+
throw new Error('@chemmangat/msal-next: tenantId is required when authorityType is "tenant"');
|
|
62
|
+
}
|
|
63
|
+
return `https://login.microsoftonline.com/${tenantId}`;
|
|
64
|
+
}
|
|
65
|
+
return `https://login.microsoftonline.com/${authorityType}`;
|
|
66
|
+
};
|
|
67
|
+
const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
|
|
68
|
+
const finalRedirectUri = redirectUri || defaultRedirectUri;
|
|
69
|
+
const msalConfig = {
|
|
70
|
+
auth: {
|
|
71
|
+
clientId,
|
|
72
|
+
authority: getAuthority(),
|
|
73
|
+
redirectUri: finalRedirectUri,
|
|
74
|
+
postLogoutRedirectUri: postLogoutRedirectUri || finalRedirectUri,
|
|
75
|
+
navigateToLoginRequestUrl
|
|
76
|
+
},
|
|
77
|
+
cache: {
|
|
78
|
+
cacheLocation,
|
|
79
|
+
storeAuthStateInCookie
|
|
80
|
+
},
|
|
81
|
+
system: {
|
|
82
|
+
loggerOptions: {
|
|
83
|
+
loggerCallback: loggerCallback || ((level, message, containsPii) => {
|
|
84
|
+
if (containsPii || !enableLogging) return;
|
|
85
|
+
switch (level) {
|
|
86
|
+
case import_msal_browser.LogLevel.Error:
|
|
87
|
+
console.error("[MSAL]", message);
|
|
88
|
+
break;
|
|
89
|
+
case import_msal_browser.LogLevel.Warning:
|
|
90
|
+
console.warn("[MSAL]", message);
|
|
91
|
+
break;
|
|
92
|
+
case import_msal_browser.LogLevel.Info:
|
|
93
|
+
console.info("[MSAL]", message);
|
|
94
|
+
break;
|
|
95
|
+
case import_msal_browser.LogLevel.Verbose:
|
|
96
|
+
console.debug("[MSAL]", message);
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}),
|
|
100
|
+
logLevel: enableLogging ? import_msal_browser.LogLevel.Verbose : import_msal_browser.LogLevel.Error
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
return msalConfig;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// src/components/MsalAuthProvider.tsx
|
|
108
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
109
|
+
function MsalAuthProvider({ children, loadingComponent, ...config }) {
|
|
110
|
+
const [msalInstance, setMsalInstance] = (0, import_react.useState)(null);
|
|
111
|
+
const instanceRef = (0, import_react.useRef)(null);
|
|
112
|
+
(0, import_react.useEffect)(() => {
|
|
113
|
+
if (instanceRef.current) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const initializeMsal = async () => {
|
|
117
|
+
try {
|
|
118
|
+
const msalConfig = createMsalConfig(config);
|
|
119
|
+
const instance = new import_msal_browser2.PublicClientApplication(msalConfig);
|
|
120
|
+
await instance.initialize();
|
|
121
|
+
const response = await instance.handleRedirectPromise();
|
|
122
|
+
if (response) {
|
|
123
|
+
console.log("[MSAL] Redirect authentication successful");
|
|
124
|
+
}
|
|
125
|
+
instance.addEventCallback((event) => {
|
|
126
|
+
if (event.eventType === import_msal_browser2.EventType.LOGIN_SUCCESS) {
|
|
127
|
+
const payload = event.payload;
|
|
128
|
+
console.log("[MSAL] Login successful:", payload.account?.username);
|
|
129
|
+
}
|
|
130
|
+
if (event.eventType === import_msal_browser2.EventType.LOGIN_FAILURE) {
|
|
131
|
+
console.error("[MSAL] Login failed:", event.error);
|
|
132
|
+
}
|
|
133
|
+
if (event.eventType === import_msal_browser2.EventType.LOGOUT_SUCCESS) {
|
|
134
|
+
console.log("[MSAL] Logout successful");
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
instanceRef.current = instance;
|
|
138
|
+
setMsalInstance(instance);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error("[MSAL] Initialization failed:", error);
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
initializeMsal();
|
|
145
|
+
}, []);
|
|
146
|
+
if (!msalInstance) {
|
|
147
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: loadingComponent || /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: "Loading authentication..." }) });
|
|
148
|
+
}
|
|
149
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_msal_react.MsalProvider, { instance: msalInstance, children });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/hooks/useMsalAuth.ts
|
|
153
|
+
var import_msal_react2 = require("@azure/msal-react");
|
|
154
|
+
var import_msal_browser3 = require("@azure/msal-browser");
|
|
155
|
+
var import_react2 = require("react");
|
|
156
|
+
function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
157
|
+
const { instance, accounts, inProgress } = (0, import_msal_react2.useMsal)();
|
|
158
|
+
const account = (0, import_msal_react2.useAccount)(accounts[0] || null);
|
|
159
|
+
const isAuthenticated = (0, import_react2.useMemo)(() => accounts.length > 0, [accounts]);
|
|
160
|
+
const loginPopup = (0, import_react2.useCallback)(
|
|
161
|
+
async (scopes = defaultScopes) => {
|
|
162
|
+
try {
|
|
163
|
+
const request = {
|
|
164
|
+
scopes,
|
|
165
|
+
prompt: "select_account"
|
|
166
|
+
};
|
|
167
|
+
await instance.loginPopup(request);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error("[MSAL] Login popup failed:", error);
|
|
170
|
+
throw error;
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
[instance, defaultScopes]
|
|
174
|
+
);
|
|
175
|
+
const loginRedirect = (0, import_react2.useCallback)(
|
|
176
|
+
async (scopes = defaultScopes) => {
|
|
177
|
+
try {
|
|
178
|
+
const request = {
|
|
179
|
+
scopes,
|
|
180
|
+
prompt: "select_account"
|
|
181
|
+
};
|
|
182
|
+
await instance.loginRedirect(request);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error("[MSAL] Login redirect failed:", error);
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
[instance, defaultScopes]
|
|
189
|
+
);
|
|
190
|
+
const logoutPopup = (0, import_react2.useCallback)(async () => {
|
|
191
|
+
try {
|
|
192
|
+
await instance.logoutPopup({
|
|
193
|
+
account: account || void 0
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.error("[MSAL] Logout popup failed:", error);
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
199
|
+
}, [instance, account]);
|
|
200
|
+
const logoutRedirect = (0, import_react2.useCallback)(async () => {
|
|
201
|
+
try {
|
|
202
|
+
await instance.logoutRedirect({
|
|
203
|
+
account: account || void 0
|
|
204
|
+
});
|
|
205
|
+
} catch (error) {
|
|
206
|
+
console.error("[MSAL] Logout redirect failed:", error);
|
|
207
|
+
throw error;
|
|
208
|
+
}
|
|
209
|
+
}, [instance, account]);
|
|
210
|
+
const acquireTokenSilent = (0, import_react2.useCallback)(
|
|
211
|
+
async (scopes = defaultScopes) => {
|
|
212
|
+
if (!account) {
|
|
213
|
+
throw new Error("[MSAL] No active account. Please login first.");
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
const request = {
|
|
217
|
+
scopes,
|
|
218
|
+
account
|
|
219
|
+
};
|
|
220
|
+
const response = await instance.acquireTokenSilent(request);
|
|
221
|
+
return response.accessToken;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error("[MSAL] Silent token acquisition failed:", error);
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
[instance, account, defaultScopes]
|
|
228
|
+
);
|
|
229
|
+
const acquireTokenPopup = (0, import_react2.useCallback)(
|
|
230
|
+
async (scopes = defaultScopes) => {
|
|
231
|
+
if (!account) {
|
|
232
|
+
throw new Error("[MSAL] No active account. Please login first.");
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const request = {
|
|
236
|
+
scopes,
|
|
237
|
+
account
|
|
238
|
+
};
|
|
239
|
+
const response = await instance.acquireTokenPopup(request);
|
|
240
|
+
return response.accessToken;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
console.error("[MSAL] Token popup acquisition failed:", error);
|
|
243
|
+
throw error;
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
[instance, account, defaultScopes]
|
|
247
|
+
);
|
|
248
|
+
const acquireTokenRedirect = (0, import_react2.useCallback)(
|
|
249
|
+
async (scopes = defaultScopes) => {
|
|
250
|
+
if (!account) {
|
|
251
|
+
throw new Error("[MSAL] No active account. Please login first.");
|
|
252
|
+
}
|
|
253
|
+
try {
|
|
254
|
+
const request = {
|
|
255
|
+
scopes,
|
|
256
|
+
account
|
|
257
|
+
};
|
|
258
|
+
await instance.acquireTokenRedirect(request);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error("[MSAL] Token redirect acquisition failed:", error);
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
[instance, account, defaultScopes]
|
|
265
|
+
);
|
|
266
|
+
const acquireToken = (0, import_react2.useCallback)(
|
|
267
|
+
async (scopes = defaultScopes) => {
|
|
268
|
+
try {
|
|
269
|
+
return await acquireTokenSilent(scopes);
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.warn("[MSAL] Silent token acquisition failed, falling back to popup");
|
|
272
|
+
return await acquireTokenPopup(scopes);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
[acquireTokenSilent, acquireTokenPopup, defaultScopes]
|
|
276
|
+
);
|
|
277
|
+
return {
|
|
278
|
+
account,
|
|
279
|
+
accounts,
|
|
280
|
+
isAuthenticated,
|
|
281
|
+
inProgress: inProgress !== import_msal_browser3.InteractionStatus.None,
|
|
282
|
+
loginPopup,
|
|
283
|
+
loginRedirect,
|
|
284
|
+
logoutPopup,
|
|
285
|
+
logoutRedirect,
|
|
286
|
+
acquireToken,
|
|
287
|
+
acquireTokenSilent,
|
|
288
|
+
acquireTokenPopup,
|
|
289
|
+
acquireTokenRedirect
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/index.ts
|
|
294
|
+
var import_msal_react3 = require("@azure/msal-react");
|
|
295
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
296
|
+
0 && (module.exports = {
|
|
297
|
+
MsalAuthProvider,
|
|
298
|
+
useAccount,
|
|
299
|
+
useIsAuthenticated,
|
|
300
|
+
useMsal,
|
|
301
|
+
useMsalAuth
|
|
302
|
+
});
|
|
303
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/components/MsalAuthProvider.tsx","../src/utils/createMsalConfig.ts","../src/hooks/useMsalAuth.ts"],"sourcesContent":["export { MsalAuthProvider } from './components/MsalAuthProvider';\r\nexport { useMsalAuth } from './hooks/useMsalAuth';\r\nexport type { MsalAuthConfig, MsalAuthProviderProps } from './types';\r\n\r\n// Re-export useful MSAL hooks\r\nexport { useMsal, useIsAuthenticated, useAccount } from '@azure/msal-react';\r\n","'use client';\r\n\r\nimport { MsalProvider } from '@azure/msal-react';\r\nimport { PublicClientApplication, EventType, EventMessage, AuthenticationResult } from '@azure/msal-browser';\r\nimport { useEffect, useState, useRef } from 'react';\r\nimport { MsalAuthProviderProps } from '../types';\r\nimport { createMsalConfig } from '../utils/createMsalConfig';\r\n\r\nexport function MsalAuthProvider({ children, loadingComponent, ...config }: MsalAuthProviderProps) {\r\n const [msalInstance, setMsalInstance] = useState<PublicClientApplication | null>(null);\r\n const instanceRef = useRef<PublicClientApplication | null>(null);\r\n\r\n useEffect(() => {\r\n // Prevent multiple initializations\r\n if (instanceRef.current) {\r\n return;\r\n }\r\n\r\n const initializeMsal = async () => {\r\n try {\r\n const msalConfig = createMsalConfig(config);\r\n const instance = new PublicClientApplication(msalConfig);\r\n \r\n await instance.initialize();\r\n\r\n // Handle redirect promise\r\n const response = await instance.handleRedirectPromise();\r\n if (response) {\r\n console.log('[MSAL] Redirect authentication successful');\r\n }\r\n\r\n // Optional: Set up event callbacks\r\n instance.addEventCallback((event: EventMessage) => {\r\n if (event.eventType === EventType.LOGIN_SUCCESS) {\r\n const payload = event.payload as AuthenticationResult;\r\n console.log('[MSAL] Login successful:', payload.account?.username);\r\n }\r\n \r\n if (event.eventType === EventType.LOGIN_FAILURE) {\r\n console.error('[MSAL] Login failed:', event.error);\r\n }\r\n\r\n if (event.eventType === EventType.LOGOUT_SUCCESS) {\r\n console.log('[MSAL] Logout successful');\r\n }\r\n });\r\n\r\n instanceRef.current = instance;\r\n setMsalInstance(instance);\r\n } catch (error) {\r\n console.error('[MSAL] Initialization failed:', error);\r\n throw error;\r\n }\r\n };\r\n\r\n initializeMsal();\r\n }, []); // Empty dependency array - only initialize once\r\n\r\n if (!msalInstance) {\r\n return <>{loadingComponent || <div>Loading authentication...</div>}</>;\r\n }\r\n\r\n return <MsalProvider instance={msalInstance}>{children}</MsalProvider>;\r\n}\r\n","import { Configuration, LogLevel } from '@azure/msal-browser';\r\nimport { MsalAuthConfig } from '../types';\r\n\r\nexport function createMsalConfig(config: MsalAuthConfig): Configuration {\r\n // If custom config provided, use it\r\n if (config.msalConfig) {\r\n return config.msalConfig;\r\n }\r\n\r\n const {\r\n clientId,\r\n tenantId,\r\n authorityType = 'common',\r\n redirectUri,\r\n postLogoutRedirectUri,\r\n cacheLocation = 'sessionStorage',\r\n storeAuthStateInCookie = false,\r\n navigateToLoginRequestUrl = true,\r\n enableLogging = false,\r\n loggerCallback,\r\n } = config;\r\n\r\n if (!clientId) {\r\n throw new Error('@chemmangat/msal-next: clientId is required');\r\n }\r\n\r\n // Build authority URL\r\n const getAuthority = (): string => {\r\n if (authorityType === 'tenant') {\r\n if (!tenantId) {\r\n throw new Error('@chemmangat/msal-next: tenantId is required when authorityType is \"tenant\"');\r\n }\r\n return `https://login.microsoftonline.com/${tenantId}`;\r\n }\r\n return `https://login.microsoftonline.com/${authorityType}`;\r\n };\r\n\r\n // Default redirect URI\r\n const defaultRedirectUri = typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000';\r\n const finalRedirectUri = redirectUri || defaultRedirectUri;\r\n\r\n const msalConfig: Configuration = {\r\n auth: {\r\n clientId,\r\n authority: getAuthority(),\r\n redirectUri: finalRedirectUri,\r\n postLogoutRedirectUri: postLogoutRedirectUri || finalRedirectUri,\r\n navigateToLoginRequestUrl,\r\n },\r\n cache: {\r\n cacheLocation,\r\n storeAuthStateInCookie,\r\n },\r\n system: {\r\n loggerOptions: {\r\n loggerCallback: loggerCallback || ((level: LogLevel, message: string, containsPii: boolean) => {\r\n if (containsPii || !enableLogging) return;\r\n \r\n switch (level) {\r\n case LogLevel.Error:\r\n console.error('[MSAL]', message);\r\n break;\r\n case LogLevel.Warning:\r\n console.warn('[MSAL]', message);\r\n break;\r\n case LogLevel.Info:\r\n console.info('[MSAL]', message);\r\n break;\r\n case LogLevel.Verbose:\r\n console.debug('[MSAL]', message);\r\n break;\r\n }\r\n }),\r\n logLevel: enableLogging ? LogLevel.Verbose : LogLevel.Error,\r\n },\r\n },\r\n };\r\n\r\n return msalConfig;\r\n}\r\n","'use client';\r\n\r\nimport { useMsal, useAccount } from '@azure/msal-react';\r\nimport { AccountInfo, InteractionStatus, PopupRequest, RedirectRequest, SilentRequest } from '@azure/msal-browser';\r\nimport { useCallback, useMemo } from 'react';\r\n\r\nexport interface UseMsalAuthReturn {\r\n /**\r\n * Current authenticated account\r\n */\r\n account: AccountInfo | null;\r\n\r\n /**\r\n * All accounts in the cache\r\n */\r\n accounts: AccountInfo[];\r\n\r\n /**\r\n * Whether user is authenticated\r\n */\r\n isAuthenticated: boolean;\r\n\r\n /**\r\n * Whether MSAL is currently performing an interaction\r\n */\r\n inProgress: boolean;\r\n\r\n /**\r\n * Login using popup\r\n */\r\n loginPopup: (scopes?: string[]) => Promise<void>;\r\n\r\n /**\r\n * Login using redirect\r\n */\r\n loginRedirect: (scopes?: string[]) => Promise<void>;\r\n\r\n /**\r\n * Logout using popup\r\n */\r\n logoutPopup: () => Promise<void>;\r\n\r\n /**\r\n * Logout using redirect\r\n */\r\n logoutRedirect: () => Promise<void>;\r\n\r\n /**\r\n * Acquire access token silently (with fallback to popup)\r\n */\r\n acquireToken: (scopes: string[]) => Promise<string>;\r\n\r\n /**\r\n * Acquire access token silently only (no fallback)\r\n */\r\n acquireTokenSilent: (scopes: string[]) => Promise<string>;\r\n\r\n /**\r\n * Acquire access token using popup\r\n */\r\n acquireTokenPopup: (scopes: string[]) => Promise<string>;\r\n\r\n /**\r\n * Acquire access token using redirect\r\n */\r\n acquireTokenRedirect: (scopes: string[]) => Promise<void>;\r\n}\r\n\r\nexport function useMsalAuth(defaultScopes: string[] = ['User.Read']): UseMsalAuthReturn {\r\n const { instance, accounts, inProgress } = useMsal();\r\n const account = useAccount(accounts[0] || null);\r\n\r\n const isAuthenticated = useMemo(() => accounts.length > 0, [accounts]);\r\n\r\n const loginPopup = useCallback(\r\n async (scopes: string[] = defaultScopes) => {\r\n try {\r\n const request: PopupRequest = {\r\n scopes,\r\n prompt: 'select_account',\r\n };\r\n await instance.loginPopup(request);\r\n } catch (error) {\r\n console.error('[MSAL] Login popup failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, defaultScopes]\r\n );\r\n\r\n const loginRedirect = useCallback(\r\n async (scopes: string[] = defaultScopes) => {\r\n try {\r\n const request: RedirectRequest = {\r\n scopes,\r\n prompt: 'select_account',\r\n };\r\n await instance.loginRedirect(request);\r\n } catch (error) {\r\n console.error('[MSAL] Login redirect failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, defaultScopes]\r\n );\r\n\r\n const logoutPopup = useCallback(async () => {\r\n try {\r\n await instance.logoutPopup({\r\n account: account || undefined,\r\n });\r\n } catch (error) {\r\n console.error('[MSAL] Logout popup failed:', error);\r\n throw error;\r\n }\r\n }, [instance, account]);\r\n\r\n const logoutRedirect = useCallback(async () => {\r\n try {\r\n await instance.logoutRedirect({\r\n account: account || undefined,\r\n });\r\n } catch (error) {\r\n console.error('[MSAL] Logout redirect failed:', error);\r\n throw error;\r\n }\r\n }, [instance, account]);\r\n\r\n const acquireTokenSilent = useCallback(\r\n async (scopes: string[] = defaultScopes): Promise<string> => {\r\n if (!account) {\r\n throw new Error('[MSAL] No active account. Please login first.');\r\n }\r\n\r\n try {\r\n const request: SilentRequest = {\r\n scopes,\r\n account,\r\n };\r\n const response = await instance.acquireTokenSilent(request);\r\n return response.accessToken;\r\n } catch (error) {\r\n console.error('[MSAL] Silent token acquisition failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, account, defaultScopes]\r\n );\r\n\r\n const acquireTokenPopup = useCallback(\r\n async (scopes: string[] = defaultScopes): Promise<string> => {\r\n if (!account) {\r\n throw new Error('[MSAL] No active account. Please login first.');\r\n }\r\n\r\n try {\r\n const request: PopupRequest = {\r\n scopes,\r\n account,\r\n };\r\n const response = await instance.acquireTokenPopup(request);\r\n return response.accessToken;\r\n } catch (error) {\r\n console.error('[MSAL] Token popup acquisition failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, account, defaultScopes]\r\n );\r\n\r\n const acquireTokenRedirect = useCallback(\r\n async (scopes: string[] = defaultScopes): Promise<void> => {\r\n if (!account) {\r\n throw new Error('[MSAL] No active account. Please login first.');\r\n }\r\n\r\n try {\r\n const request: RedirectRequest = {\r\n scopes,\r\n account,\r\n };\r\n await instance.acquireTokenRedirect(request);\r\n } catch (error) {\r\n console.error('[MSAL] Token redirect acquisition failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, account, defaultScopes]\r\n );\r\n\r\n const acquireToken = useCallback(\r\n async (scopes: string[] = defaultScopes): Promise<string> => {\r\n try {\r\n return await acquireTokenSilent(scopes);\r\n } catch (error) {\r\n console.warn('[MSAL] Silent token acquisition failed, falling back to popup');\r\n return await acquireTokenPopup(scopes);\r\n }\r\n },\r\n [acquireTokenSilent, acquireTokenPopup, defaultScopes]\r\n );\r\n\r\n return {\r\n account,\r\n accounts,\r\n isAuthenticated,\r\n inProgress: inProgress !== InteractionStatus.None,\r\n loginPopup,\r\n loginRedirect,\r\n logoutPopup,\r\n logoutRedirect,\r\n acquireToken,\r\n acquireTokenSilent,\r\n acquireTokenPopup,\r\n acquireTokenRedirect,\r\n };\r\n}\r\n"],"mappings":"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","names":["import_msal_browser","import_msal_react","import_msal_browser","import_react","import_msal_react"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/components/MsalAuthProvider.tsx
|
|
4
|
+
import { MsalProvider } from "@azure/msal-react";
|
|
5
|
+
import { PublicClientApplication, EventType } from "@azure/msal-browser";
|
|
6
|
+
import { useEffect, useState, useRef } from "react";
|
|
7
|
+
|
|
8
|
+
// src/utils/createMsalConfig.ts
|
|
9
|
+
import { LogLevel } from "@azure/msal-browser";
|
|
10
|
+
function createMsalConfig(config) {
|
|
11
|
+
if (config.msalConfig) {
|
|
12
|
+
return config.msalConfig;
|
|
13
|
+
}
|
|
14
|
+
const {
|
|
15
|
+
clientId,
|
|
16
|
+
tenantId,
|
|
17
|
+
authorityType = "common",
|
|
18
|
+
redirectUri,
|
|
19
|
+
postLogoutRedirectUri,
|
|
20
|
+
cacheLocation = "sessionStorage",
|
|
21
|
+
storeAuthStateInCookie = false,
|
|
22
|
+
navigateToLoginRequestUrl = true,
|
|
23
|
+
enableLogging = false,
|
|
24
|
+
loggerCallback
|
|
25
|
+
} = config;
|
|
26
|
+
if (!clientId) {
|
|
27
|
+
throw new Error("@chemmangat/msal-next: clientId is required");
|
|
28
|
+
}
|
|
29
|
+
const getAuthority = () => {
|
|
30
|
+
if (authorityType === "tenant") {
|
|
31
|
+
if (!tenantId) {
|
|
32
|
+
throw new Error('@chemmangat/msal-next: tenantId is required when authorityType is "tenant"');
|
|
33
|
+
}
|
|
34
|
+
return `https://login.microsoftonline.com/${tenantId}`;
|
|
35
|
+
}
|
|
36
|
+
return `https://login.microsoftonline.com/${authorityType}`;
|
|
37
|
+
};
|
|
38
|
+
const defaultRedirectUri = typeof window !== "undefined" ? window.location.origin : "http://localhost:3000";
|
|
39
|
+
const finalRedirectUri = redirectUri || defaultRedirectUri;
|
|
40
|
+
const msalConfig = {
|
|
41
|
+
auth: {
|
|
42
|
+
clientId,
|
|
43
|
+
authority: getAuthority(),
|
|
44
|
+
redirectUri: finalRedirectUri,
|
|
45
|
+
postLogoutRedirectUri: postLogoutRedirectUri || finalRedirectUri,
|
|
46
|
+
navigateToLoginRequestUrl
|
|
47
|
+
},
|
|
48
|
+
cache: {
|
|
49
|
+
cacheLocation,
|
|
50
|
+
storeAuthStateInCookie
|
|
51
|
+
},
|
|
52
|
+
system: {
|
|
53
|
+
loggerOptions: {
|
|
54
|
+
loggerCallback: loggerCallback || ((level, message, containsPii) => {
|
|
55
|
+
if (containsPii || !enableLogging) return;
|
|
56
|
+
switch (level) {
|
|
57
|
+
case LogLevel.Error:
|
|
58
|
+
console.error("[MSAL]", message);
|
|
59
|
+
break;
|
|
60
|
+
case LogLevel.Warning:
|
|
61
|
+
console.warn("[MSAL]", message);
|
|
62
|
+
break;
|
|
63
|
+
case LogLevel.Info:
|
|
64
|
+
console.info("[MSAL]", message);
|
|
65
|
+
break;
|
|
66
|
+
case LogLevel.Verbose:
|
|
67
|
+
console.debug("[MSAL]", message);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}),
|
|
71
|
+
logLevel: enableLogging ? LogLevel.Verbose : LogLevel.Error
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
return msalConfig;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/components/MsalAuthProvider.tsx
|
|
79
|
+
import { Fragment, jsx } from "react/jsx-runtime";
|
|
80
|
+
function MsalAuthProvider({ children, loadingComponent, ...config }) {
|
|
81
|
+
const [msalInstance, setMsalInstance] = useState(null);
|
|
82
|
+
const instanceRef = useRef(null);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
if (instanceRef.current) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const initializeMsal = async () => {
|
|
88
|
+
try {
|
|
89
|
+
const msalConfig = createMsalConfig(config);
|
|
90
|
+
const instance = new PublicClientApplication(msalConfig);
|
|
91
|
+
await instance.initialize();
|
|
92
|
+
const response = await instance.handleRedirectPromise();
|
|
93
|
+
if (response) {
|
|
94
|
+
console.log("[MSAL] Redirect authentication successful");
|
|
95
|
+
}
|
|
96
|
+
instance.addEventCallback((event) => {
|
|
97
|
+
if (event.eventType === EventType.LOGIN_SUCCESS) {
|
|
98
|
+
const payload = event.payload;
|
|
99
|
+
console.log("[MSAL] Login successful:", payload.account?.username);
|
|
100
|
+
}
|
|
101
|
+
if (event.eventType === EventType.LOGIN_FAILURE) {
|
|
102
|
+
console.error("[MSAL] Login failed:", event.error);
|
|
103
|
+
}
|
|
104
|
+
if (event.eventType === EventType.LOGOUT_SUCCESS) {
|
|
105
|
+
console.log("[MSAL] Logout successful");
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
instanceRef.current = instance;
|
|
109
|
+
setMsalInstance(instance);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error("[MSAL] Initialization failed:", error);
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
initializeMsal();
|
|
116
|
+
}, []);
|
|
117
|
+
if (!msalInstance) {
|
|
118
|
+
return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent || /* @__PURE__ */ jsx("div", { children: "Loading authentication..." }) });
|
|
119
|
+
}
|
|
120
|
+
return /* @__PURE__ */ jsx(MsalProvider, { instance: msalInstance, children });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// src/hooks/useMsalAuth.ts
|
|
124
|
+
import { useMsal, useAccount } from "@azure/msal-react";
|
|
125
|
+
import { InteractionStatus } from "@azure/msal-browser";
|
|
126
|
+
import { useCallback, useMemo } from "react";
|
|
127
|
+
function useMsalAuth(defaultScopes = ["User.Read"]) {
|
|
128
|
+
const { instance, accounts, inProgress } = useMsal();
|
|
129
|
+
const account = useAccount(accounts[0] || null);
|
|
130
|
+
const isAuthenticated = useMemo(() => accounts.length > 0, [accounts]);
|
|
131
|
+
const loginPopup = useCallback(
|
|
132
|
+
async (scopes = defaultScopes) => {
|
|
133
|
+
try {
|
|
134
|
+
const request = {
|
|
135
|
+
scopes,
|
|
136
|
+
prompt: "select_account"
|
|
137
|
+
};
|
|
138
|
+
await instance.loginPopup(request);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error("[MSAL] Login popup failed:", error);
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
[instance, defaultScopes]
|
|
145
|
+
);
|
|
146
|
+
const loginRedirect = useCallback(
|
|
147
|
+
async (scopes = defaultScopes) => {
|
|
148
|
+
try {
|
|
149
|
+
const request = {
|
|
150
|
+
scopes,
|
|
151
|
+
prompt: "select_account"
|
|
152
|
+
};
|
|
153
|
+
await instance.loginRedirect(request);
|
|
154
|
+
} catch (error) {
|
|
155
|
+
console.error("[MSAL] Login redirect failed:", error);
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
[instance, defaultScopes]
|
|
160
|
+
);
|
|
161
|
+
const logoutPopup = useCallback(async () => {
|
|
162
|
+
try {
|
|
163
|
+
await instance.logoutPopup({
|
|
164
|
+
account: account || void 0
|
|
165
|
+
});
|
|
166
|
+
} catch (error) {
|
|
167
|
+
console.error("[MSAL] Logout popup failed:", error);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}, [instance, account]);
|
|
171
|
+
const logoutRedirect = useCallback(async () => {
|
|
172
|
+
try {
|
|
173
|
+
await instance.logoutRedirect({
|
|
174
|
+
account: account || void 0
|
|
175
|
+
});
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error("[MSAL] Logout redirect failed:", error);
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
}, [instance, account]);
|
|
181
|
+
const acquireTokenSilent = useCallback(
|
|
182
|
+
async (scopes = defaultScopes) => {
|
|
183
|
+
if (!account) {
|
|
184
|
+
throw new Error("[MSAL] No active account. Please login first.");
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const request = {
|
|
188
|
+
scopes,
|
|
189
|
+
account
|
|
190
|
+
};
|
|
191
|
+
const response = await instance.acquireTokenSilent(request);
|
|
192
|
+
return response.accessToken;
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error("[MSAL] Silent token acquisition failed:", error);
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
[instance, account, defaultScopes]
|
|
199
|
+
);
|
|
200
|
+
const acquireTokenPopup = useCallback(
|
|
201
|
+
async (scopes = defaultScopes) => {
|
|
202
|
+
if (!account) {
|
|
203
|
+
throw new Error("[MSAL] No active account. Please login first.");
|
|
204
|
+
}
|
|
205
|
+
try {
|
|
206
|
+
const request = {
|
|
207
|
+
scopes,
|
|
208
|
+
account
|
|
209
|
+
};
|
|
210
|
+
const response = await instance.acquireTokenPopup(request);
|
|
211
|
+
return response.accessToken;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error("[MSAL] Token popup acquisition failed:", error);
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
[instance, account, defaultScopes]
|
|
218
|
+
);
|
|
219
|
+
const acquireTokenRedirect = useCallback(
|
|
220
|
+
async (scopes = defaultScopes) => {
|
|
221
|
+
if (!account) {
|
|
222
|
+
throw new Error("[MSAL] No active account. Please login first.");
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
const request = {
|
|
226
|
+
scopes,
|
|
227
|
+
account
|
|
228
|
+
};
|
|
229
|
+
await instance.acquireTokenRedirect(request);
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error("[MSAL] Token redirect acquisition failed:", error);
|
|
232
|
+
throw error;
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
[instance, account, defaultScopes]
|
|
236
|
+
);
|
|
237
|
+
const acquireToken = useCallback(
|
|
238
|
+
async (scopes = defaultScopes) => {
|
|
239
|
+
try {
|
|
240
|
+
return await acquireTokenSilent(scopes);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
console.warn("[MSAL] Silent token acquisition failed, falling back to popup");
|
|
243
|
+
return await acquireTokenPopup(scopes);
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
[acquireTokenSilent, acquireTokenPopup, defaultScopes]
|
|
247
|
+
);
|
|
248
|
+
return {
|
|
249
|
+
account,
|
|
250
|
+
accounts,
|
|
251
|
+
isAuthenticated,
|
|
252
|
+
inProgress: inProgress !== InteractionStatus.None,
|
|
253
|
+
loginPopup,
|
|
254
|
+
loginRedirect,
|
|
255
|
+
logoutPopup,
|
|
256
|
+
logoutRedirect,
|
|
257
|
+
acquireToken,
|
|
258
|
+
acquireTokenSilent,
|
|
259
|
+
acquireTokenPopup,
|
|
260
|
+
acquireTokenRedirect
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/index.ts
|
|
265
|
+
import { useMsal as useMsal2, useIsAuthenticated, useAccount as useAccount2 } from "@azure/msal-react";
|
|
266
|
+
export {
|
|
267
|
+
MsalAuthProvider,
|
|
268
|
+
useAccount2 as useAccount,
|
|
269
|
+
useIsAuthenticated,
|
|
270
|
+
useMsal2 as useMsal,
|
|
271
|
+
useMsalAuth
|
|
272
|
+
};
|
|
273
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/MsalAuthProvider.tsx","../src/utils/createMsalConfig.ts","../src/hooks/useMsalAuth.ts","../src/index.ts"],"sourcesContent":["'use client';\r\n\r\nimport { MsalProvider } from '@azure/msal-react';\r\nimport { PublicClientApplication, EventType, EventMessage, AuthenticationResult } from '@azure/msal-browser';\r\nimport { useEffect, useState, useRef } from 'react';\r\nimport { MsalAuthProviderProps } from '../types';\r\nimport { createMsalConfig } from '../utils/createMsalConfig';\r\n\r\nexport function MsalAuthProvider({ children, loadingComponent, ...config }: MsalAuthProviderProps) {\r\n const [msalInstance, setMsalInstance] = useState<PublicClientApplication | null>(null);\r\n const instanceRef = useRef<PublicClientApplication | null>(null);\r\n\r\n useEffect(() => {\r\n // Prevent multiple initializations\r\n if (instanceRef.current) {\r\n return;\r\n }\r\n\r\n const initializeMsal = async () => {\r\n try {\r\n const msalConfig = createMsalConfig(config);\r\n const instance = new PublicClientApplication(msalConfig);\r\n \r\n await instance.initialize();\r\n\r\n // Handle redirect promise\r\n const response = await instance.handleRedirectPromise();\r\n if (response) {\r\n console.log('[MSAL] Redirect authentication successful');\r\n }\r\n\r\n // Optional: Set up event callbacks\r\n instance.addEventCallback((event: EventMessage) => {\r\n if (event.eventType === EventType.LOGIN_SUCCESS) {\r\n const payload = event.payload as AuthenticationResult;\r\n console.log('[MSAL] Login successful:', payload.account?.username);\r\n }\r\n \r\n if (event.eventType === EventType.LOGIN_FAILURE) {\r\n console.error('[MSAL] Login failed:', event.error);\r\n }\r\n\r\n if (event.eventType === EventType.LOGOUT_SUCCESS) {\r\n console.log('[MSAL] Logout successful');\r\n }\r\n });\r\n\r\n instanceRef.current = instance;\r\n setMsalInstance(instance);\r\n } catch (error) {\r\n console.error('[MSAL] Initialization failed:', error);\r\n throw error;\r\n }\r\n };\r\n\r\n initializeMsal();\r\n }, []); // Empty dependency array - only initialize once\r\n\r\n if (!msalInstance) {\r\n return <>{loadingComponent || <div>Loading authentication...</div>}</>;\r\n }\r\n\r\n return <MsalProvider instance={msalInstance}>{children}</MsalProvider>;\r\n}\r\n","import { Configuration, LogLevel } from '@azure/msal-browser';\r\nimport { MsalAuthConfig } from '../types';\r\n\r\nexport function createMsalConfig(config: MsalAuthConfig): Configuration {\r\n // If custom config provided, use it\r\n if (config.msalConfig) {\r\n return config.msalConfig;\r\n }\r\n\r\n const {\r\n clientId,\r\n tenantId,\r\n authorityType = 'common',\r\n redirectUri,\r\n postLogoutRedirectUri,\r\n cacheLocation = 'sessionStorage',\r\n storeAuthStateInCookie = false,\r\n navigateToLoginRequestUrl = true,\r\n enableLogging = false,\r\n loggerCallback,\r\n } = config;\r\n\r\n if (!clientId) {\r\n throw new Error('@chemmangat/msal-next: clientId is required');\r\n }\r\n\r\n // Build authority URL\r\n const getAuthority = (): string => {\r\n if (authorityType === 'tenant') {\r\n if (!tenantId) {\r\n throw new Error('@chemmangat/msal-next: tenantId is required when authorityType is \"tenant\"');\r\n }\r\n return `https://login.microsoftonline.com/${tenantId}`;\r\n }\r\n return `https://login.microsoftonline.com/${authorityType}`;\r\n };\r\n\r\n // Default redirect URI\r\n const defaultRedirectUri = typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000';\r\n const finalRedirectUri = redirectUri || defaultRedirectUri;\r\n\r\n const msalConfig: Configuration = {\r\n auth: {\r\n clientId,\r\n authority: getAuthority(),\r\n redirectUri: finalRedirectUri,\r\n postLogoutRedirectUri: postLogoutRedirectUri || finalRedirectUri,\r\n navigateToLoginRequestUrl,\r\n },\r\n cache: {\r\n cacheLocation,\r\n storeAuthStateInCookie,\r\n },\r\n system: {\r\n loggerOptions: {\r\n loggerCallback: loggerCallback || ((level: LogLevel, message: string, containsPii: boolean) => {\r\n if (containsPii || !enableLogging) return;\r\n \r\n switch (level) {\r\n case LogLevel.Error:\r\n console.error('[MSAL]', message);\r\n break;\r\n case LogLevel.Warning:\r\n console.warn('[MSAL]', message);\r\n break;\r\n case LogLevel.Info:\r\n console.info('[MSAL]', message);\r\n break;\r\n case LogLevel.Verbose:\r\n console.debug('[MSAL]', message);\r\n break;\r\n }\r\n }),\r\n logLevel: enableLogging ? LogLevel.Verbose : LogLevel.Error,\r\n },\r\n },\r\n };\r\n\r\n return msalConfig;\r\n}\r\n","'use client';\r\n\r\nimport { useMsal, useAccount } from '@azure/msal-react';\r\nimport { AccountInfo, InteractionStatus, PopupRequest, RedirectRequest, SilentRequest } from '@azure/msal-browser';\r\nimport { useCallback, useMemo } from 'react';\r\n\r\nexport interface UseMsalAuthReturn {\r\n /**\r\n * Current authenticated account\r\n */\r\n account: AccountInfo | null;\r\n\r\n /**\r\n * All accounts in the cache\r\n */\r\n accounts: AccountInfo[];\r\n\r\n /**\r\n * Whether user is authenticated\r\n */\r\n isAuthenticated: boolean;\r\n\r\n /**\r\n * Whether MSAL is currently performing an interaction\r\n */\r\n inProgress: boolean;\r\n\r\n /**\r\n * Login using popup\r\n */\r\n loginPopup: (scopes?: string[]) => Promise<void>;\r\n\r\n /**\r\n * Login using redirect\r\n */\r\n loginRedirect: (scopes?: string[]) => Promise<void>;\r\n\r\n /**\r\n * Logout using popup\r\n */\r\n logoutPopup: () => Promise<void>;\r\n\r\n /**\r\n * Logout using redirect\r\n */\r\n logoutRedirect: () => Promise<void>;\r\n\r\n /**\r\n * Acquire access token silently (with fallback to popup)\r\n */\r\n acquireToken: (scopes: string[]) => Promise<string>;\r\n\r\n /**\r\n * Acquire access token silently only (no fallback)\r\n */\r\n acquireTokenSilent: (scopes: string[]) => Promise<string>;\r\n\r\n /**\r\n * Acquire access token using popup\r\n */\r\n acquireTokenPopup: (scopes: string[]) => Promise<string>;\r\n\r\n /**\r\n * Acquire access token using redirect\r\n */\r\n acquireTokenRedirect: (scopes: string[]) => Promise<void>;\r\n}\r\n\r\nexport function useMsalAuth(defaultScopes: string[] = ['User.Read']): UseMsalAuthReturn {\r\n const { instance, accounts, inProgress } = useMsal();\r\n const account = useAccount(accounts[0] || null);\r\n\r\n const isAuthenticated = useMemo(() => accounts.length > 0, [accounts]);\r\n\r\n const loginPopup = useCallback(\r\n async (scopes: string[] = defaultScopes) => {\r\n try {\r\n const request: PopupRequest = {\r\n scopes,\r\n prompt: 'select_account',\r\n };\r\n await instance.loginPopup(request);\r\n } catch (error) {\r\n console.error('[MSAL] Login popup failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, defaultScopes]\r\n );\r\n\r\n const loginRedirect = useCallback(\r\n async (scopes: string[] = defaultScopes) => {\r\n try {\r\n const request: RedirectRequest = {\r\n scopes,\r\n prompt: 'select_account',\r\n };\r\n await instance.loginRedirect(request);\r\n } catch (error) {\r\n console.error('[MSAL] Login redirect failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, defaultScopes]\r\n );\r\n\r\n const logoutPopup = useCallback(async () => {\r\n try {\r\n await instance.logoutPopup({\r\n account: account || undefined,\r\n });\r\n } catch (error) {\r\n console.error('[MSAL] Logout popup failed:', error);\r\n throw error;\r\n }\r\n }, [instance, account]);\r\n\r\n const logoutRedirect = useCallback(async () => {\r\n try {\r\n await instance.logoutRedirect({\r\n account: account || undefined,\r\n });\r\n } catch (error) {\r\n console.error('[MSAL] Logout redirect failed:', error);\r\n throw error;\r\n }\r\n }, [instance, account]);\r\n\r\n const acquireTokenSilent = useCallback(\r\n async (scopes: string[] = defaultScopes): Promise<string> => {\r\n if (!account) {\r\n throw new Error('[MSAL] No active account. Please login first.');\r\n }\r\n\r\n try {\r\n const request: SilentRequest = {\r\n scopes,\r\n account,\r\n };\r\n const response = await instance.acquireTokenSilent(request);\r\n return response.accessToken;\r\n } catch (error) {\r\n console.error('[MSAL] Silent token acquisition failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, account, defaultScopes]\r\n );\r\n\r\n const acquireTokenPopup = useCallback(\r\n async (scopes: string[] = defaultScopes): Promise<string> => {\r\n if (!account) {\r\n throw new Error('[MSAL] No active account. Please login first.');\r\n }\r\n\r\n try {\r\n const request: PopupRequest = {\r\n scopes,\r\n account,\r\n };\r\n const response = await instance.acquireTokenPopup(request);\r\n return response.accessToken;\r\n } catch (error) {\r\n console.error('[MSAL] Token popup acquisition failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, account, defaultScopes]\r\n );\r\n\r\n const acquireTokenRedirect = useCallback(\r\n async (scopes: string[] = defaultScopes): Promise<void> => {\r\n if (!account) {\r\n throw new Error('[MSAL] No active account. Please login first.');\r\n }\r\n\r\n try {\r\n const request: RedirectRequest = {\r\n scopes,\r\n account,\r\n };\r\n await instance.acquireTokenRedirect(request);\r\n } catch (error) {\r\n console.error('[MSAL] Token redirect acquisition failed:', error);\r\n throw error;\r\n }\r\n },\r\n [instance, account, defaultScopes]\r\n );\r\n\r\n const acquireToken = useCallback(\r\n async (scopes: string[] = defaultScopes): Promise<string> => {\r\n try {\r\n return await acquireTokenSilent(scopes);\r\n } catch (error) {\r\n console.warn('[MSAL] Silent token acquisition failed, falling back to popup');\r\n return await acquireTokenPopup(scopes);\r\n }\r\n },\r\n [acquireTokenSilent, acquireTokenPopup, defaultScopes]\r\n );\r\n\r\n return {\r\n account,\r\n accounts,\r\n isAuthenticated,\r\n inProgress: inProgress !== InteractionStatus.None,\r\n loginPopup,\r\n loginRedirect,\r\n logoutPopup,\r\n logoutRedirect,\r\n acquireToken,\r\n acquireTokenSilent,\r\n acquireTokenPopup,\r\n acquireTokenRedirect,\r\n };\r\n}\r\n","export { MsalAuthProvider } from './components/MsalAuthProvider';\r\nexport { useMsalAuth } from './hooks/useMsalAuth';\r\nexport type { MsalAuthConfig, MsalAuthProviderProps } from './types';\r\n\r\n// Re-export useful MSAL hooks\r\nexport { useMsal, useIsAuthenticated, useAccount } from '@azure/msal-react';\r\n"],"mappings":"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","names":["useMsal","useAccount"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chemmangat/msal-next",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Fully configurable MSAL authentication package for Next.js App Router",
|
|
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
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"msal",
|
|
26
|
+
"nextjs",
|
|
27
|
+
"authentication",
|
|
28
|
+
"azure-ad",
|
|
29
|
+
"microsoft",
|
|
30
|
+
"oauth",
|
|
31
|
+
"next.js",
|
|
32
|
+
"app-router",
|
|
33
|
+
"typescript"
|
|
34
|
+
],
|
|
35
|
+
"author": "Chemmangat",
|
|
36
|
+
"license": "MIT",
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/chemmangat/msal-next.git",
|
|
40
|
+
"directory": "packages/core"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/chemmangat/msal-next#readme",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/chemmangat/msal-next/issues"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@azure/msal-browser": "^3.11.0",
|
|
48
|
+
"@azure/msal-react": "^2.0.0",
|
|
49
|
+
"next": ">=14.0.0",
|
|
50
|
+
"react": ">=18.0.0",
|
|
51
|
+
"react-dom": ">=18.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@azure/msal-browser": "^3.11.1",
|
|
55
|
+
"@azure/msal-react": "^2.0.15",
|
|
56
|
+
"@types/react": "^18.2.0",
|
|
57
|
+
"react": "^18.2.0",
|
|
58
|
+
"tsup": "^8.0.1",
|
|
59
|
+
"typescript": "^5.3.0"
|
|
60
|
+
}
|
|
61
|
+
}
|