Skip to content

Commit 3d9e739

Browse files
committed
feat: get token
1 parent 7f52781 commit 3d9e739

File tree

5 files changed

+113
-65
lines changed

5 files changed

+113
-65
lines changed

libs/lib-msal-react/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './lib/button-login/button-login';
33
export * from './lib/button-logout/button-logout';
44
export * from './lib/account-context/account-context';
55
export * from './lib/verify-token/verify-token';
6+
export * from './lib/get-token/get-token';
Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,44 @@
1-
import { createContext, useContext, useEffect, useState } from 'react';
2-
import { useMsal } from '@azure/msal-react';
1+
'use client'
2+
3+
import { createContext, useEffect, useState } from 'react';
4+
import { useMsal, useIsAuthenticated } from '@azure/msal-react';
35

46
type AccountState = {
57
username: string | null;
68
isLoading: boolean;
79
};
810

11+
const AccountDefaultState: AccountState = {
12+
username: null,
13+
isLoading: true,
14+
};
15+
916
/**
1017
* Provides a React context for managing authentication using MSAL.
1118
* This component wraps your application and provides access to the MSAL instance and authentication state.
1219
* It also handles authentication and token acquisition using MSAL.
1320
* @see https://react.dev/learn/passing-data-deeply-with-context
1421
*/
15-
export const AccountContext = createContext<AccountState>({ username: null, isLoading: true });
22+
const AccountContext = createContext<AccountState>(AccountDefaultState);
1623

17-
// Create a provider component
18-
export const AccountProvider: React.FC<{ children: React.ReactNode }> = ({
24+
const AccountProvider: React.FC<{ children: React.ReactNode }> = ({
1925
children,
2026
}) => {
2127
const { inProgress, accounts } = useMsal();
22-
const [account, setAccount] = useState<AccountState>({ username: null, isLoading: true });
28+
const [account, setAccount] = useState<AccountState>(AccountDefaultState);
29+
const isAuthenticated = useIsAuthenticated();
2330

2431
useEffect(() => {
25-
console.log(accounts);
2632
if (inProgress === 'none' && accounts.length > 0) {
2733
setAccount({ username: accounts[0].name ?? null, isLoading: false });
2834
}
29-
}, [inProgress, accounts]);
35+
}, [inProgress, accounts, isAuthenticated]);
3036

3137
return (
32-
<AccountContext.Provider
33-
value={account}
34-
>
38+
<AccountContext.Provider value={account}>
3539
{children}
3640
</AccountContext.Provider>
3741
);
3842
};
3943

40-
// Create a hook to use the context
41-
export const useAccount = () => {
42-
return useContext(AccountContext);
43-
};
44+
export { AccountContext, AccountProvider };
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { loginRequest } from "../msal-react";
2+
import { InteractionRequiredAuthError, PublicClientApplication, AccountInfo } from "@azure/msal-browser";
3+
4+
export async function getToken(msalInstance: PublicClientApplication, account: AccountInfo): Promise<string | null> {
5+
if (!account) {
6+
throw Error("No active account! Verify a user has been signed in and setActiveAccount has been called.");
7+
}
8+
9+
try {
10+
const response = await msalInstance.acquireTokenSilent({
11+
...loginRequest,
12+
account: account
13+
});
14+
15+
console.log('response:', response)
16+
17+
if (!response || !response.accessToken) {
18+
console.error('Failed to acquire token.');
19+
return null;
20+
}
21+
22+
return response.accessToken;
23+
} catch (error) {
24+
if (error instanceof InteractionRequiredAuthError) {
25+
// fallback to interaction when silent call fails
26+
try {
27+
const response = await msalInstance.acquireTokenPopup({
28+
...loginRequest,
29+
account: account
30+
});
31+
32+
if (response === null || response.accessToken === undefined) {
33+
console.error('Failed to acquire token.');
34+
return null;
35+
}
36+
37+
return response.accessToken;
38+
} catch (err) {
39+
console.error('Error acquiring token:', err);
40+
return null;
41+
}
42+
} else {
43+
console.error('Error acquiring token:', error);
44+
return null;
45+
}
46+
}
47+
}

libs/lib-msal-react/src/lib/msal-react.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,11 @@ const b2cPolicies = {
2828
authorityDomain: `${b2cAuthority}.b2clogin.com`,
2929
};
3030

31-
// MSAL Configuration
31+
export const loginRequest = {
32+
scopes: [`https://${b2cAuthority}.onmicrosoft.com/${process.env.NEXT_PUBLIC_AZURE_AD_CLIENT_ID}/ReadUser`]
33+
};
34+
35+
// MSAL Configurationcc
3236
const msalConfig: Configuration = {
3337
auth: {
3438
clientId: process.env.NEXT_PUBLIC_AZURE_AD_CLIENT_ID || '',
@@ -53,10 +57,10 @@ const msalConfig: Configuration = {
5357
console.error(message);
5458
return;
5559
case LogLevel.Info:
56-
console.info(message);
60+
// console.info(message);
5761
return;
5862
case LogLevel.Verbose:
59-
console.debug(message);
63+
// console.debug(message);
6064
return;
6165
case LogLevel.Warning:
6266
console.warn(message);
Lines changed: 42 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,53 @@
1-
import React, { useEffect, useState } from 'react';
2-
import { useAccount } from '@my-workspace/lib-msal-react';
3-
import styles from './account-summary.module.css';
1+
// Import necessary libraries
2+
import React, { useContext, useEffect, useState } from 'react';
3+
import axios from 'axios';
4+
import { useMsal } from "@azure/msal-react";
5+
import { AccountContext, getToken } from '@my-workspace/lib-msal-react';
6+
import { PublicClientApplication } from '@azure/msal-browser';
47

5-
// eslint-disable-next-line @typescript-eslint/no-empty-interface
6-
export interface AccountSummaryProps {}
8+
/**
9+
* This component displays a summary of the user's account.
10+
* It uses the AccountContext to access the user's username.
11+
*/
12+
export const AccountSummary: React.FC = () => {
13+
// Use the useContext hook to access the AccountContext
14+
const { username, isLoading } = useContext(AccountContext);
15+
const { inProgress, instance, accounts } = useMsal();
716

8-
export function AccountSummary(props: AccountSummaryProps) {
9-
const { accessToken } = useAccount();
10-
const [data, setData] = useState(null);
11-
const [loading, setLoading] = useState(false);
12-
const [error, setError] = useState('');
17+
// State to hold the API response
18+
const [apiResponse, setApiResponse] = useState(null);
1319

20+
// Use the useEffect hook to call the API when the component mounts
1421
useEffect(() => {
15-
if (!accessToken) {
16-
setError('You must be logged in to view this information.');
17-
return;
18-
}
22+
if (inProgress === 'none' && accounts.length > 0) {
23+
const fetchTokenAndData = async () => {
24+
const token = await getToken(instance as PublicClientApplication, accounts[0]); // Replace scopes as needed
25+
if (token) {
26+
axios.get('/api/hello-msal', {
27+
headers: {
28+
Authorization: `Bearer ${token}`
29+
}
30+
})
31+
.then(response => setApiResponse(response.data))
32+
.catch(error => console.error(error));
33+
}
34+
};
1935

20-
setLoading(true);
21-
fetch('/api/hello-msal', { // Adjust the API endpoint as necessary
22-
method: 'GET',
23-
headers: {
24-
'Authorization': `Bearer ${accessToken}`,
25-
'Content-Type': 'application/json'
26-
},
27-
})
28-
.then(response => {
29-
if (!response.ok) {
30-
throw new Error('Failed to fetch protected data.');
31-
}
32-
return response.json();
33-
})
34-
.then(data => {
35-
setData(data);
36-
setError('');
37-
})
38-
.catch(error => {
39-
console.error("Error fetching protected data:", error);
40-
setError(error.message);
41-
})
42-
.finally(() => setLoading(false));
43-
}, [accessToken]);
36+
fetchTokenAndData();
37+
}
38+
}, [inProgress, instance, accounts]);
4439

40+
// Render the component
4541
return (
46-
<div className={styles.container}>
47-
<h1>Welcome to AccountSummary!</h1>
48-
{error && <p className={styles.error}>{error}</p>}
49-
{loading ? (
50-
<p>Loading account information...</p>
42+
<div className="text-white">
43+
{isLoading ? (
44+
<p>Loading...</p>
45+
) : username ? (
46+
<p>Welcome, {username}!</p>
5147
) : (
52-
data && <pre>{JSON.stringify(data, null, 2)}</pre>
48+
<p>Please sign in.</p>
5349
)}
50+
{apiResponse && <p>{apiResponse}</p>}
5451
</div>
5552
);
56-
}
57-
58-
export default AccountSummary;
53+
};

0 commit comments

Comments
 (0)