@conduit-d365/auth 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AuthProvider.d.ts +27 -0
- package/dist/AuthProvider.d.ts.map +1 -0
- package/dist/AuthProvider.js +51 -0
- package/dist/AuthProvider.js.map +1 -0
- package/dist/ProtectedRoute.d.ts +23 -0
- package/dist/ProtectedRoute.d.ts.map +1 -0
- package/dist/ProtectedRoute.js +28 -0
- package/dist/ProtectedRoute.js.map +1 -0
- package/dist/apiClient.d.ts +23 -0
- package/dist/apiClient.d.ts.map +1 -0
- package/dist/apiClient.js +46 -0
- package/dist/apiClient.js.map +1 -0
- package/dist/authConfig.d.ts +15 -0
- package/dist/authConfig.d.ts.map +1 -0
- package/dist/authConfig.js +27 -0
- package/dist/authConfig.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/useAuth.d.ts +27 -0
- package/dist/useAuth.d.ts.map +1 -0
- package/dist/useAuth.js +70 -0
- package/dist/useAuth.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
/** Context for passing custom scopes from AuthProvider to useAuth */
|
|
3
|
+
export declare const ScopesContext: React.Context<string[] | undefined>;
|
|
4
|
+
interface AuthProviderProps {
|
|
5
|
+
/** Entra ID app registration client ID */
|
|
6
|
+
clientId: string;
|
|
7
|
+
/** OAuth redirect URI. Defaults to window.location.origin */
|
|
8
|
+
redirectUri?: string;
|
|
9
|
+
/** Custom OAuth scopes. Defaults to ["api://<clientId>/access_as_user"] */
|
|
10
|
+
scopes?: string[];
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Wraps the app in an MSAL authentication context.
|
|
15
|
+
*
|
|
16
|
+
* Handles MSAL initialisation, redirect callbacks, and active account
|
|
17
|
+
* management. All child components can use the useAuth() hook to access
|
|
18
|
+
* the authenticated user.
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* <AuthProvider clientId={import.meta.env.VITE_ENTRA_CLIENT_ID}>
|
|
22
|
+
* <App />
|
|
23
|
+
* </AuthProvider>
|
|
24
|
+
*/
|
|
25
|
+
export declare function AuthProvider({ clientId, redirectUri, scopes, children, }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=AuthProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAC;AAahE,qEAAqE;AACrE,eAAO,MAAM,aAAa,qCAAiD,CAAC;AAE5E,UAAU,iBAAiB;IACzB,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,WAAW,EACX,MAAM,EACN,QAAQ,GACT,EAAE,iBAAiB,2CA+CnB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useEffect, useRef } from "react";
|
|
3
|
+
import { MsalProvider, } from "@azure/msal-react";
|
|
4
|
+
import { EventType, PublicClientApplication, } from "@azure/msal-browser";
|
|
5
|
+
import { createMsalConfig } from "./authConfig";
|
|
6
|
+
/** Context for passing custom scopes from AuthProvider to useAuth */
|
|
7
|
+
export const ScopesContext = createContext(undefined);
|
|
8
|
+
/**
|
|
9
|
+
* Wraps the app in an MSAL authentication context.
|
|
10
|
+
*
|
|
11
|
+
* Handles MSAL initialisation, redirect callbacks, and active account
|
|
12
|
+
* management. All child components can use the useAuth() hook to access
|
|
13
|
+
* the authenticated user.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* <AuthProvider clientId={import.meta.env.VITE_ENTRA_CLIENT_ID}>
|
|
17
|
+
* <App />
|
|
18
|
+
* </AuthProvider>
|
|
19
|
+
*/
|
|
20
|
+
export function AuthProvider({ clientId, redirectUri, scopes, children, }) {
|
|
21
|
+
const msalInstanceRef = useRef(null);
|
|
22
|
+
if (!msalInstanceRef.current) {
|
|
23
|
+
const config = createMsalConfig({ clientId, redirectUri });
|
|
24
|
+
msalInstanceRef.current = new PublicClientApplication(config);
|
|
25
|
+
}
|
|
26
|
+
const msalInstance = msalInstanceRef.current;
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const init = async () => {
|
|
29
|
+
await msalInstance.initialize();
|
|
30
|
+
await msalInstance.handleRedirectPromise();
|
|
31
|
+
// Set active account if one exists but isn't set
|
|
32
|
+
if (!msalInstance.getActiveAccount() &&
|
|
33
|
+
msalInstance.getAllAccounts().length > 0) {
|
|
34
|
+
msalInstance.setActiveAccount(msalInstance.getAllAccounts()[0]);
|
|
35
|
+
}
|
|
36
|
+
// Set active account on successful login
|
|
37
|
+
msalInstance.addEventCallback((event) => {
|
|
38
|
+
if (event.eventType === EventType.LOGIN_SUCCESS &&
|
|
39
|
+
event.payload) {
|
|
40
|
+
const result = event.payload;
|
|
41
|
+
if (result.account) {
|
|
42
|
+
msalInstance.setActiveAccount(result.account);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
init();
|
|
48
|
+
}, [msalInstance]);
|
|
49
|
+
return (_jsx(MsalProvider, { instance: msalInstance, children: _jsx(ScopesContext.Provider, { value: scopes, children: children }) }));
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=AuthProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthProvider.js","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAChE,OAAO,EACL,YAAY,GAEb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,SAAS,EACT,uBAAuB,GAGxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,qEAAqE;AACrE,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAuB,SAAS,CAAC,CAAC;AAY5E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,EAC3B,QAAQ,EACR,WAAW,EACX,MAAM,EACN,QAAQ,GACU;IAClB,MAAM,eAAe,GAAG,MAAM,CAAiC,IAAI,CAAC,CAAC;IAErE,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;QAC3D,eAAe,CAAC,OAAO,GAAG,IAAI,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,CAAC;IAE7C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;YACtB,MAAM,YAAY,CAAC,UAAU,EAAE,CAAC;YAChC,MAAM,YAAY,CAAC,qBAAqB,EAAE,CAAC;YAE3C,iDAAiD;YACjD,IACE,CAAC,YAAY,CAAC,gBAAgB,EAAE;gBAChC,YAAY,CAAC,cAAc,EAAE,CAAC,MAAM,GAAG,CAAC,EACxC,CAAC;gBACD,YAAY,CAAC,gBAAgB,CAAC,YAAY,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,CAAC;YAED,yCAAyC;YACzC,YAAY,CAAC,gBAAgB,CAAC,CAAC,KAAmB,EAAE,EAAE;gBACpD,IACE,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,aAAa;oBAC3C,KAAK,CAAC,OAAO,EACb,CAAC;oBACD,MAAM,MAAM,GAAG,KAAK,CAAC,OAA+B,CAAC;oBACrD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;wBACnB,YAAY,CAAC,gBAAgB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;oBAChD,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,IAAI,EAAE,CAAC;IACT,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,OAAO,CACL,KAAC,YAAY,IAAC,QAAQ,EAAE,YAAY,YAClC,KAAC,aAAa,CAAC,QAAQ,IAAC,KAAK,EAAE,MAAM,YAClC,QAAQ,GACc,GACZ,CAChB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
interface ProtectedRouteProps {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
/** Custom loading component. Defaults to a simple "Loading..." div. */
|
|
5
|
+
loadingComponent?: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Route guard that requires authentication.
|
|
9
|
+
*
|
|
10
|
+
* While MSAL is initialising, shows a loading indicator. If the user
|
|
11
|
+
* is not authenticated, triggers a login redirect. Otherwise renders
|
|
12
|
+
* the children.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* <Route path="/projects" element={
|
|
16
|
+
* <ProtectedRoute>
|
|
17
|
+
* <ProjectsPage />
|
|
18
|
+
* </ProtectedRoute>
|
|
19
|
+
* } />
|
|
20
|
+
*/
|
|
21
|
+
export declare function ProtectedRoute({ children, loadingComponent, }: ProtectedRouteProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=ProtectedRoute.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProtectedRoute.d.ts","sourceRoot":"","sources":["../src/ProtectedRoute.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACpC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,gBAAgB,GACjB,EAAE,mBAAmB,2CAarB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth } from "./useAuth";
|
|
3
|
+
/**
|
|
4
|
+
* Route guard that requires authentication.
|
|
5
|
+
*
|
|
6
|
+
* While MSAL is initialising, shows a loading indicator. If the user
|
|
7
|
+
* is not authenticated, triggers a login redirect. Otherwise renders
|
|
8
|
+
* the children.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* <Route path="/projects" element={
|
|
12
|
+
* <ProtectedRoute>
|
|
13
|
+
* <ProjectsPage />
|
|
14
|
+
* </ProtectedRoute>
|
|
15
|
+
* } />
|
|
16
|
+
*/
|
|
17
|
+
export function ProtectedRoute({ children, loadingComponent, }) {
|
|
18
|
+
const { isAuthenticated, isLoading, login } = useAuth();
|
|
19
|
+
if (isLoading) {
|
|
20
|
+
return _jsx(_Fragment, { children: loadingComponent ?? _jsx("div", { children: "Loading..." }) });
|
|
21
|
+
}
|
|
22
|
+
if (!isAuthenticated) {
|
|
23
|
+
login();
|
|
24
|
+
return _jsx("div", { children: "Redirecting to login..." });
|
|
25
|
+
}
|
|
26
|
+
return _jsx(_Fragment, { children: children });
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=ProtectedRoute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProtectedRoute.js","sourceRoot":"","sources":["../src/ProtectedRoute.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,QAAQ,EACR,gBAAgB,GACI;IACpB,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC;IAExD,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,4BAAG,gBAAgB,IAAI,uCAAqB,GAAI,CAAC;IAC1D,CAAC;IAED,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,KAAK,EAAE,CAAC;QACR,OAAO,oDAAkC,CAAC;IAC5C,CAAC;IAED,OAAO,4BAAG,QAAQ,GAAI,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an authenticated fetch wrapper.
|
|
3
|
+
*
|
|
4
|
+
* Automatically acquires a Bearer token via MSAL and attaches it to
|
|
5
|
+
* every request. Replaces ConfigCompare's `credentials: 'include'`
|
|
6
|
+
* cookie-based pattern with JWT auth.
|
|
7
|
+
*
|
|
8
|
+
* @param getAccessToken - Function that returns a valid access token
|
|
9
|
+
* @returns An authenticated fetch function
|
|
10
|
+
*/
|
|
11
|
+
export declare function createApiClient(getAccessToken: () => Promise<string>): (endpoint: string, options?: RequestInit) => Promise<Response>;
|
|
12
|
+
/**
|
|
13
|
+
* React hook that provides an authenticated API client.
|
|
14
|
+
*
|
|
15
|
+
* Drop-in replacement for ConfigCompare's apiCall() utility.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* const api = useApiClient();
|
|
19
|
+
* const resp = await api("/api/projects");
|
|
20
|
+
* const projects = await resp.json();
|
|
21
|
+
*/
|
|
22
|
+
export declare function useApiClient(): (endpoint: string, options?: RequestInit) => Promise<Response>;
|
|
23
|
+
//# sourceMappingURL=apiClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiClient.d.ts","sourceRoot":"","sources":["../src/apiClient.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,IAGnC,UAAU,MAAM,EAChB,UAAS,WAAgB,KACxB,OAAO,CAAC,QAAQ,CAAC,CAsBrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,eApCd,MAAM,YACP,WAAW,KACnB,OAAO,CAAC,QAAQ,CAAC,CAqCrB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useAuth } from "./useAuth";
|
|
2
|
+
/**
|
|
3
|
+
* Create an authenticated fetch wrapper.
|
|
4
|
+
*
|
|
5
|
+
* Automatically acquires a Bearer token via MSAL and attaches it to
|
|
6
|
+
* every request. Replaces ConfigCompare's `credentials: 'include'`
|
|
7
|
+
* cookie-based pattern with JWT auth.
|
|
8
|
+
*
|
|
9
|
+
* @param getAccessToken - Function that returns a valid access token
|
|
10
|
+
* @returns An authenticated fetch function
|
|
11
|
+
*/
|
|
12
|
+
export function createApiClient(getAccessToken) {
|
|
13
|
+
return async (endpoint, options = {}) => {
|
|
14
|
+
const token = await getAccessToken();
|
|
15
|
+
// Use relative URLs in production (container nginx proxies /api/*).
|
|
16
|
+
// In development, VITE_API_URL can point to the local backend.
|
|
17
|
+
const apiBase = typeof import.meta !== "undefined" &&
|
|
18
|
+
import.meta.env?.VITE_API_URL
|
|
19
|
+
? import.meta.env.VITE_API_URL
|
|
20
|
+
: "";
|
|
21
|
+
const headers = new Headers(options.headers);
|
|
22
|
+
headers.set("Authorization", `Bearer ${token}`);
|
|
23
|
+
if (!headers.has("Content-Type")) {
|
|
24
|
+
headers.set("Content-Type", "application/json");
|
|
25
|
+
}
|
|
26
|
+
return fetch(`${apiBase}${endpoint}`, {
|
|
27
|
+
...options,
|
|
28
|
+
headers,
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* React hook that provides an authenticated API client.
|
|
34
|
+
*
|
|
35
|
+
* Drop-in replacement for ConfigCompare's apiCall() utility.
|
|
36
|
+
*
|
|
37
|
+
* Usage:
|
|
38
|
+
* const api = useApiClient();
|
|
39
|
+
* const resp = await api("/api/projects");
|
|
40
|
+
* const projects = await resp.json();
|
|
41
|
+
*/
|
|
42
|
+
export function useApiClient() {
|
|
43
|
+
const { getAccessToken } = useAuth();
|
|
44
|
+
return createApiClient(getAccessToken);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=apiClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apiClient.js","sourceRoot":"","sources":["../src/apiClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,cAAqC;IAErC,OAAO,KAAK,EACV,QAAgB,EAChB,UAAuB,EAAE,EACN,EAAE;QACrB,MAAM,KAAK,GAAG,MAAM,cAAc,EAAE,CAAC;QAErC,oEAAoE;QACpE,+DAA+D;QAC/D,MAAM,OAAO,GACX,OAAO,MAAM,CAAC,IAAI,KAAK,WAAW;YAClC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY;YAC3B,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY;YAC9B,CAAC,CAAC,EAAE,CAAC;QAET,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,KAAK,CAAC,GAAG,OAAO,GAAG,QAAQ,EAAE,EAAE;YACpC,GAAG,OAAO;YACV,OAAO;SACR,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,EAAE,cAAc,EAAE,GAAG,OAAO,EAAE,CAAC;IACrC,OAAO,eAAe,CAAC,cAAc,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Configuration } from "@azure/msal-browser";
|
|
2
|
+
import type { ConduitAuthConfig } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Create an MSAL configuration for multi-tenant Entra ID auth.
|
|
5
|
+
*
|
|
6
|
+
* Uses the /common authority endpoint so users from any Azure AD tenant
|
|
7
|
+
* can sign in — MSAL redirects each user to their home tenant's login page.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createMsalConfig(config: ConduitAuthConfig): Configuration;
|
|
10
|
+
/**
|
|
11
|
+
* Get the default OAuth scopes for API access.
|
|
12
|
+
* Uses the custom scope exposed by the Conduit app registration.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getDefaultScopes(clientId: string): string[];
|
|
15
|
+
//# sourceMappingURL=authConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authConfig.d.ts","sourceRoot":"","sources":["../src/authConfig.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa,CAYzE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAE3D"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create an MSAL configuration for multi-tenant Entra ID auth.
|
|
3
|
+
*
|
|
4
|
+
* Uses the /common authority endpoint so users from any Azure AD tenant
|
|
5
|
+
* can sign in — MSAL redirects each user to their home tenant's login page.
|
|
6
|
+
*/
|
|
7
|
+
export function createMsalConfig(config) {
|
|
8
|
+
return {
|
|
9
|
+
auth: {
|
|
10
|
+
clientId: config.clientId,
|
|
11
|
+
authority: "https://login.microsoftonline.com/common",
|
|
12
|
+
redirectUri: config.redirectUri || window.location.origin,
|
|
13
|
+
postLogoutRedirectUri: window.location.origin,
|
|
14
|
+
},
|
|
15
|
+
cache: {
|
|
16
|
+
cacheLocation: "sessionStorage",
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get the default OAuth scopes for API access.
|
|
22
|
+
* Uses the custom scope exposed by the Conduit app registration.
|
|
23
|
+
*/
|
|
24
|
+
export function getDefaultScopes(clientId) {
|
|
25
|
+
return [`api://${clientId}/access_as_user`];
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=authConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authConfig.js","sourceRoot":"","sources":["../src/authConfig.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAyB;IACxD,OAAO;QACL,IAAI,EAAE;YACJ,QAAQ,EAAE,MAAM,CAAC,QAAQ;YACzB,SAAS,EAAE,0CAA0C;YACrD,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM;YACzD,qBAAqB,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM;SAC9C;QACD,KAAK,EAAE;YACL,aAAa,EAAE,gBAAgB;SAChC;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,OAAO,CAAC,SAAS,QAAQ,iBAAiB,CAAC,CAAC;AAC9C,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { AuthProvider } from "./AuthProvider";
|
|
2
|
+
export { useAuth } from "./useAuth";
|
|
3
|
+
export type { UseAuthReturn } from "./useAuth";
|
|
4
|
+
export { ProtectedRoute } from "./ProtectedRoute";
|
|
5
|
+
export { createApiClient, useApiClient } from "./apiClient";
|
|
6
|
+
export { createMsalConfig, getDefaultScopes } from "./authConfig";
|
|
7
|
+
export type { AuthUser, ConduitAuthConfig } from "./types";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAClE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { AuthProvider } from "./AuthProvider";
|
|
2
|
+
export { useAuth } from "./useAuth";
|
|
3
|
+
export { ProtectedRoute } from "./ProtectedRoute";
|
|
4
|
+
export { createApiClient, useApiClient } from "./apiClient";
|
|
5
|
+
export { createMsalConfig, getDefaultScopes } from "./authConfig";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authenticated user identity from Entra ID token claims.
|
|
3
|
+
* Maps to the backend's AuthenticatedUser dataclass.
|
|
4
|
+
*/
|
|
5
|
+
export interface AuthUser {
|
|
6
|
+
/** Entra ID tenant ID (tid claim) — identifies the user's organisation */
|
|
7
|
+
tenantId: string;
|
|
8
|
+
/** User object ID (oid claim) — unique within their tenant */
|
|
9
|
+
userId: string;
|
|
10
|
+
/** User's email address (preferred_username claim) */
|
|
11
|
+
email: string;
|
|
12
|
+
/** User's display name (name claim) */
|
|
13
|
+
displayName: string;
|
|
14
|
+
}
|
|
15
|
+
/** Configuration for the Conduit auth provider. */
|
|
16
|
+
export interface ConduitAuthConfig {
|
|
17
|
+
/** Entra ID app registration client ID */
|
|
18
|
+
clientId: string;
|
|
19
|
+
/** OAuth redirect URI. Defaults to window.location.origin */
|
|
20
|
+
redirectUri?: string;
|
|
21
|
+
/** OAuth scopes. Defaults to ["api://<clientId>/access_as_user"] */
|
|
22
|
+
scopes?: string[];
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,QAAQ;IACvB,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,mDAAmD;AACnD,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { AuthUser } from "./types";
|
|
2
|
+
export interface UseAuthReturn {
|
|
3
|
+
/** The authenticated user, or null if not signed in */
|
|
4
|
+
user: AuthUser | null;
|
|
5
|
+
/** Whether the user is authenticated */
|
|
6
|
+
isAuthenticated: boolean;
|
|
7
|
+
/** Whether MSAL is still initialising */
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
/** Trigger a login redirect */
|
|
10
|
+
login: () => Promise<void>;
|
|
11
|
+
/** Trigger a logout redirect */
|
|
12
|
+
logout: () => Promise<void>;
|
|
13
|
+
/** Acquire an access token for API calls (silently, or via redirect) */
|
|
14
|
+
getAccessToken: () => Promise<string>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Hook for accessing auth state and operations.
|
|
18
|
+
*
|
|
19
|
+
* Extracts user identity from MSAL account claims (tenant ID, user ID,
|
|
20
|
+
* email, display name). Provides token acquisition for authenticated
|
|
21
|
+
* API calls.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* const { user, isAuthenticated, login, getAccessToken } = useAuth();
|
|
25
|
+
*/
|
|
26
|
+
export declare function useAuth(): UseAuthReturn;
|
|
27
|
+
//# sourceMappingURL=useAuth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAuth.d.ts","sourceRoot":"","sources":["../src/useAuth.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAIxC,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,wCAAwC;IACxC,eAAe,EAAE,OAAO,CAAC;IACzB,yCAAyC;IACzC,SAAS,EAAE,OAAO,CAAC;IACnB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,wEAAwE;IACxE,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CACvC;AAED;;;;;;;;;GASG;AACH,wBAAgB,OAAO,IAAI,aAAa,CAyDvC"}
|
package/dist/useAuth.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { useMsal, useAccount } from "@azure/msal-react";
|
|
3
|
+
import { InteractionRequiredAuthError } from "@azure/msal-browser";
|
|
4
|
+
import { getDefaultScopes } from "./authConfig";
|
|
5
|
+
import { ScopesContext } from "./AuthProvider";
|
|
6
|
+
/**
|
|
7
|
+
* Hook for accessing auth state and operations.
|
|
8
|
+
*
|
|
9
|
+
* Extracts user identity from MSAL account claims (tenant ID, user ID,
|
|
10
|
+
* email, display name). Provides token acquisition for authenticated
|
|
11
|
+
* API calls.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const { user, isAuthenticated, login, getAccessToken } = useAuth();
|
|
15
|
+
*/
|
|
16
|
+
export function useAuth() {
|
|
17
|
+
const { instance, accounts, inProgress } = useMsal();
|
|
18
|
+
const account = useAccount(accounts[0] ?? null);
|
|
19
|
+
const isLoading = inProgress !== "none";
|
|
20
|
+
const customScopes = useContext(ScopesContext);
|
|
21
|
+
const user = account
|
|
22
|
+
? {
|
|
23
|
+
tenantId: account.idTokenClaims?.tid ?? "",
|
|
24
|
+
userId: account.idTokenClaims?.oid ?? "",
|
|
25
|
+
email: account.username ?? "",
|
|
26
|
+
displayName: account.name ?? "",
|
|
27
|
+
}
|
|
28
|
+
: null;
|
|
29
|
+
const login = async () => {
|
|
30
|
+
const clientId = instance.getConfiguration().auth.clientId;
|
|
31
|
+
await instance.loginRedirect({
|
|
32
|
+
scopes: customScopes ?? getDefaultScopes(clientId),
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
const logout = async () => {
|
|
36
|
+
await instance.logoutRedirect();
|
|
37
|
+
};
|
|
38
|
+
const getAccessToken = async () => {
|
|
39
|
+
if (!account) {
|
|
40
|
+
throw new Error("No authenticated account");
|
|
41
|
+
}
|
|
42
|
+
const clientId = instance.getConfiguration().auth.clientId;
|
|
43
|
+
try {
|
|
44
|
+
const result = await instance.acquireTokenSilent({
|
|
45
|
+
scopes: customScopes ?? getDefaultScopes(clientId),
|
|
46
|
+
account,
|
|
47
|
+
});
|
|
48
|
+
return result.accessToken;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (error instanceof InteractionRequiredAuthError) {
|
|
52
|
+
// Token can't be refreshed silently — redirect to login
|
|
53
|
+
await instance.acquireTokenRedirect({
|
|
54
|
+
scopes: customScopes ?? getDefaultScopes(clientId),
|
|
55
|
+
account,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
user,
|
|
63
|
+
isAuthenticated: !!account,
|
|
64
|
+
isLoading,
|
|
65
|
+
login,
|
|
66
|
+
logout,
|
|
67
|
+
getAccessToken,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=useAuth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAuth.js","sourceRoot":"","sources":["../src/useAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAEnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAiB/C;;;;;;;;;GASG;AACH,MAAM,UAAU,OAAO;IACrB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAAC;IACrD,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,UAAU,KAAK,MAAM,CAAC;IACxC,MAAM,YAAY,GAAG,UAAU,CAAC,aAAa,CAAC,CAAC;IAE/C,MAAM,IAAI,GAAoB,OAAO;QACnC,CAAC,CAAC;YACE,QAAQ,EAAG,OAAO,CAAC,aAAa,EAAE,GAAc,IAAI,EAAE;YACtD,MAAM,EAAG,OAAO,CAAC,aAAa,EAAE,GAAc,IAAI,EAAE;YACpD,KAAK,EAAE,OAAO,CAAC,QAAQ,IAAI,EAAE;YAC7B,WAAW,EAAE,OAAO,CAAC,IAAI,IAAI,EAAE;SAChC;QACH,CAAC,CAAC,IAAI,CAAC;IAET,MAAM,KAAK,GAAG,KAAK,IAAI,EAAE;QACvB,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC3D,MAAM,QAAQ,CAAC,aAAa,CAAC;YAC3B,MAAM,EAAE,YAAY,IAAI,gBAAgB,CAAC,QAAQ,CAAC;SACnD,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE;QACxB,MAAM,QAAQ,CAAC,cAAc,EAAE,CAAC;IAClC,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,KAAK,IAAqB,EAAE;QACjD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC9C,CAAC;QACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC3D,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,kBAAkB,CAAC;gBAC/C,MAAM,EAAE,YAAY,IAAI,gBAAgB,CAAC,QAAQ,CAAC;gBAClD,OAAO;aACR,CAAC,CAAC;YACH,OAAO,MAAM,CAAC,WAAW,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,4BAA4B,EAAE,CAAC;gBAClD,wDAAwD;gBACxD,MAAM,QAAQ,CAAC,oBAAoB,CAAC;oBAClC,MAAM,EAAE,YAAY,IAAI,gBAAgB,CAAC,QAAQ,CAAC;oBAClD,OAAO;iBACR,CAAC,CAAC;YACL,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,IAAI;QACJ,eAAe,EAAE,CAAC,CAAC,OAAO;QAC1B,SAAS;QACT,KAAK;QACL,MAAM;QACN,cAAc;KACf,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@conduit-d365/auth",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Shared Entra ID authentication React components for the Conduit suite",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@azure/msal-browser": "^3.0 || ^4.0 || ^5.0",
|
|
20
|
+
"@azure/msal-react": "^2.0 || ^3.0 || ^5.0",
|
|
21
|
+
"react": "^18.0 || ^19.0",
|
|
22
|
+
"react-dom": "^18.0 || ^19.0",
|
|
23
|
+
"react-router-dom": "^6.0 || ^7.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@azure/msal-browser": "^5.0",
|
|
27
|
+
"@azure/msal-react": "^5.0",
|
|
28
|
+
"@types/react": "^19.0",
|
|
29
|
+
"react": "^19.0",
|
|
30
|
+
"react-dom": "^19.0",
|
|
31
|
+
"react-router-dom": "^7.0",
|
|
32
|
+
"typescript": "^5.5"
|
|
33
|
+
}
|
|
34
|
+
}
|