Auzure AD B2C for Authentication and Authorization

Hi All, I am trying to set up auth with Azure AD. After a successful login on Azure AD B2C, I am redirected back to the login page unless I refresh and then I am redirected to the Dashboad page.
Omer
Omer577d ago
Hey @.dark.energy 👋, I am glad to see you ⚡️ Check the auth status in the checkAuth function of the authProvider. If user logged in you can return with "Promise.resolve". There is a similar implementation for supabase https://github.com/refinedev/refine/blob/next/examples/dataProvider/supabase/src/App.tsx#L94
GitHub
refine/App.tsx at next · refinedev/refine
Build your React-based CRUD applications, without constraints. - refine/App.tsx at next · refinedev/refine
absent-sapphire
absent-sapphire577d ago
Thanks @Omer I am doing that but seems like checkAuth doesn't get called after a successful redirect. Msal.js has events that you can hook into where set the activeaccount.
const authProvider: AuthProvider = {
login: () => { return Promise.resolve(false) },
register: () => Promise.resolve(),
resetPassword: () => Promise.resolve(),
updatePassword: () => Promise.resolve(),
logout: () => Promise.resolve(),
checkAuth: () => {
if (isAuthenticated) return Promise.resolve();
try {
if (account) {
// Silently acquires an access token which is then attached to a request for API access
instance.acquireTokenSilent(request).then((response) => {
console.log(response)
localStorage.setItem(TOKEN_KEY, response.accessToken)
}).catch((e) => {
instance.acquireTokenPopup(request).then((response) => {
localStorage.setItem(TOKEN_KEY, response.accessToken)
});
});
return Promise.resolve();
} else {
return Promise.reject();
}
}
catch (error) {
return Promise.reject();
}
},
checkError: () => Promise.resolve(),
getPermissions: () => Promise.resolve(),
getUserIdentity: async (): Promise<AccountInfo> => {
if (account === null || account === undefined) {
return Promise.reject();
}
return Promise.resolve(account);
},
};
const authProvider: AuthProvider = {
login: () => { return Promise.resolve(false) },
register: () => Promise.resolve(),
resetPassword: () => Promise.resolve(),
updatePassword: () => Promise.resolve(),
logout: () => Promise.resolve(),
checkAuth: () => {
if (isAuthenticated) return Promise.resolve();
try {
if (account) {
// Silently acquires an access token which is then attached to a request for API access
instance.acquireTokenSilent(request).then((response) => {
console.log(response)
localStorage.setItem(TOKEN_KEY, response.accessToken)
}).catch((e) => {
instance.acquireTokenPopup(request).then((response) => {
localStorage.setItem(TOKEN_KEY, response.accessToken)
});
});
return Promise.resolve();
} else {
return Promise.reject();
}
}
catch (error) {
return Promise.reject();
}
},
checkError: () => Promise.resolve(),
getPermissions: () => Promise.resolve(),
getUserIdentity: async (): Promise<AccountInfo> => {
if (account === null || account === undefined) {
return Promise.reject();
}
return Promise.resolve(account);
},
};
msal callback
const msalInstance = new PublicClientApplication(msalConfig);


msalInstance.addEventCallback(event => {
if (event.eventType === EventType.LOGIN_SUCCESS) {
// alert("Successful Login")
const payload: EventPayload = event.payload;
msalInstance.setActiveAccount(payload as AccountInfo)

let account = msalInstance.getActiveAccount()

const request: SilentRequest = {
...tokenRequest,
account: account!
};

// Silently acquires an access token which is then attached to a request for API access
msalInstance.acquireTokenSilent(request).then((response) => {
console.log("Fetching access token: success")
console.log("Scopes", response.scopes)
console.log("Token Type", response.tokenType)

localStorage.setItem(TOKEN_KEY, response.accessToken)
}).catch((e) => {
msalInstance.acquireTokenPopup(request).then((response) => {
localStorage.setItem(TOKEN_KEY, response.accessToken)
});
});
}

})
const msalInstance = new PublicClientApplication(msalConfig);


msalInstance.addEventCallback(event => {
if (event.eventType === EventType.LOGIN_SUCCESS) {
// alert("Successful Login")
const payload: EventPayload = event.payload;
msalInstance.setActiveAccount(payload as AccountInfo)

let account = msalInstance.getActiveAccount()

const request: SilentRequest = {
...tokenRequest,
account: account!
};

// Silently acquires an access token which is then attached to a request for API access
msalInstance.acquireTokenSilent(request).then((response) => {
console.log("Fetching access token: success")
console.log("Scopes", response.scopes)
console.log("Token Type", response.tokenType)

localStorage.setItem(TOKEN_KEY, response.accessToken)
}).catch((e) => {
msalInstance.acquireTokenPopup(request).then((response) => {
localStorage.setItem(TOKEN_KEY, response.accessToken)
});
});
}

})
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider("http://localhost:8000/api/v1", axiosInstance)}
authProvider={authProvider}
LoginPage={LoginPage}
Layout={Layout}
DashboardPage={DashboardPage}
resources={[
{
name: "documents",
list: DocumentList,
},
]}
options={{
disableTelemetry: true
}} />
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider("http://localhost:8000/api/v1", axiosInstance)}
authProvider={authProvider}
LoginPage={LoginPage}
Layout={Layout}
DashboardPage={DashboardPage}
resources={[
{
name: "documents",
list: DocumentList,
},
]}
options={{
disableTelemetry: true
}} />
Omer
Omer577d ago
Can you add "console.log" at the top of checkAuth? You can also check our Auth0 example, it works similarly https://github.com/refinedev/refine/blob/next/examples/authProvider/auth0/src/App.tsx#L36 https://refine.dev/docs/examples/auth-provider/auth0/
absent-sapphire
absent-sapphire577d ago
I have followed the Auth0 example.
absent-sapphire
absent-sapphire577d ago
No description
absent-sapphire
absent-sapphire577d ago
Now, if I were to refresh this screen, then I get redirected to Dashboard
Omer
Omer577d ago
Probably returning Promise.reject from "checkAuth" after redirect. You should wait for the result of the "acquireTokenSilent" and "acquireTokenPopup" async functions. Could you try like this
checkAuth: async () => {
if (isAuthenticated) return Promise.resolve();
try {
if (account) {
// Silently acquires an access token which is then attached to a request for API access
try {
const token = await instance.acquireTokenSilent(request)
localStorage.setItem(TOKEN_KEY, response.accessToken)
return Promise.resolve();

}
catch(e) {
return Promise.reject();
}

}
catch (error) {
return Promise.reject();
}
},
checkAuth: async () => {
if (isAuthenticated) return Promise.resolve();
try {
if (account) {
// Silently acquires an access token which is then attached to a request for API access
try {
const token = await instance.acquireTokenSilent(request)
localStorage.setItem(TOKEN_KEY, response.accessToken)
return Promise.resolve();

}
catch(e) {
return Promise.reject();
}

}
catch (error) {
return Promise.reject();
}
},
absent-sapphire
absent-sapphire577d ago
Okay
checkAuth: async () => {
try {
if (account) {
// Silently acquires an access token which is then attached to a request for API access
try {
const token = await instance.acquireTokenSilent(request)
localStorage.setItem(TOKEN_KEY, token.accessToken)
return Promise.resolve();
}
catch (e) {
return Promise.reject();
}
}
else {
return Promise.reject();
}
}
catch (error) {
return Promise.reject();
}
},
checkAuth: async () => {
try {
if (account) {
// Silently acquires an access token which is then attached to a request for API access
try {
const token = await instance.acquireTokenSilent(request)
localStorage.setItem(TOKEN_KEY, token.accessToken)
return Promise.resolve();
}
catch (e) {
return Promise.reject();
}
}
else {
return Promise.reject();
}
}
catch (error) {
return Promise.reject();
}
},
I needed an else in if otherwise it was going straight to Dashboard But this leads to the same behaviour
Omer
Omer577d ago
Could you try this?
checkAuth: async () => {
try {
const token = await instance.acquireTokenSilent(request)
localStorage.setItem(TOKEN_KEY, token.accessToken)
return Promise.resolve();
}
catch (e) {
return Promise.reject();
}
},
checkAuth: async () => {
try {
const token = await instance.acquireTokenSilent(request)
localStorage.setItem(TOKEN_KEY, token.accessToken)
return Promise.resolve();
}
catch (e) {
return Promise.reject();
}
},
if it doesn't work i will try to create an example
absent-sapphire
absent-sapphire577d ago
Not working
Omer
Omer577d ago
Tutorial: Create a React single-page app that uses auth code flow -...
In this tutorial, you create a React SPA that can sign in users and use the auth code flow to obtain an access token from the Microsoft identity platform and call the Microsoft Graph API.
absent-sapphire
absent-sapphire577d ago
import { Configuration, LogLevel } from "@azure/msal-browser";

export const msalConfig: Configuration = {
auth: {
clientId: `${process.env.REACT_APP_AZURE_AAD_CLIENT_ID}`,
authority: `https://${process.env.REACT_APP_AZURE_AAD_TENANT_NAME}.b2clogin.com/${process.env.REACT_APP_AZURE_AAD_TENANT_NAME}.onmicrosoft.com/${process.env.REACT_APP_AZURE_AAD_POLICY_NAME}`,
knownAuthorities: [`${process.env.REACT_APP_AZURE_AAD_TENANT_NAME}.b2clogin.com`],
redirectUri: "http://localhost:3000/",
postLogoutRedirectUri: window.location.origin,
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
},
};

// Add scopes here for ID token to be used at Microsoft identity platform endpoints.
export const loginRequest = {
scopes: ["User.Read"]
};

export const tokenRequest = {
scopes: [
"https://xx.onmicrosoft.com/api/read"
]
};


// Add the endpoints here for Microsoft Graph API services you'd like to use.
export const graphConfig = {
graphMeEndpoint: "Enter_the_Graph_Endpoint_Here/v1.0/me"
};
import { Configuration, LogLevel } from "@azure/msal-browser";

export const msalConfig: Configuration = {
auth: {
clientId: `${process.env.REACT_APP_AZURE_AAD_CLIENT_ID}`,
authority: `https://${process.env.REACT_APP_AZURE_AAD_TENANT_NAME}.b2clogin.com/${process.env.REACT_APP_AZURE_AAD_TENANT_NAME}.onmicrosoft.com/${process.env.REACT_APP_AZURE_AAD_POLICY_NAME}`,
knownAuthorities: [`${process.env.REACT_APP_AZURE_AAD_TENANT_NAME}.b2clogin.com`],
redirectUri: "http://localhost:3000/",
postLogoutRedirectUri: window.location.origin,
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
},
};

// Add scopes here for ID token to be used at Microsoft identity platform endpoints.
export const loginRequest = {
scopes: ["User.Read"]
};

export const tokenRequest = {
scopes: [
"https://xx.onmicrosoft.com/api/read"
]
};


// Add the endpoints here for Microsoft Graph API services you'd like to use.
export const graphConfig = {
graphMeEndpoint: "Enter_the_Graph_Endpoint_Here/v1.0/me"
};
Omer
Omer577d ago
Is it possible to share information with me via private message? I can quickly skip the setup steps. REACT_APP_AZURE_AAD_CLIENT_ID, REACT_APP_AZURE_AAD_TENANT_NAME etc
absent-sapphire
absent-sapphire577d ago
Sure
Omer
Omer577d ago
Thank you! 🎉 Ok I found the problem. Could you try this?
const App: React.FC = () => {
const { instance, accounts, inProgress } = useMsal();

if (inProgress === "login" || inProgress === "handleRedirect") {
return <div>Loading...</div>;
}
const App: React.FC = () => {
const { instance, accounts, inProgress } = useMsal();

if (inProgress === "login" || inProgress === "handleRedirect") {
return <div>Loading...</div>;
}
checkAuth: async () => {
try {
const token = await instance.acquireTokenSilent(request);
localStorage.setItem("TOKEN_KEY", token.accessToken);
return Promise.resolve();
} catch (e) {
console.log("error", { e });
return Promise.reject();
}
},
checkAuth: async () => {
try {
const token = await instance.acquireTokenSilent(request);
localStorage.setItem("TOKEN_KEY", token.accessToken);
return Promise.resolve();
} catch (e) {
console.log("error", { e });
return Promise.reject();
}
},
More Posts
queryOptionsI want to hit an Api with a specific route, for exampe, lets say api/user-directories/ for example Add smooth scroll effect in iconsHey everyone I want to made some changes in footer section of the this website but I'm not able to fHide *Button for forbidden actionsHi! Is there a way to avoid the disabled button for actions an user cannot do (because unauthorized)difference between importing from refinecore and refineantdfor example i have a component useSelect so if i import it from refinecore it dont have same props cI am getting 301 status using refine-simple-rest although there is not any error on postman.I am using refine-simple-rest as my data provider to connect with the django rest API but I get backchange label of breadcrumbi am using breadcrumb in create page but i am getting its as patients/create i want to change labeluseSelect onSearch without call API cos i got limit call APIcan i call API once and search query without recall API?How to submit a modal form to create a Comment when you are in the Post show page?Given the fact that I'm a first timer using React, Building refine app has been amazing. What I'd liMaking duplicate calls in the apiUsing the github example (finefoods - mui), the application is making duplicate calls in the api Docustom route element not visible{ element:<div> <LayoutWrapper>sssshhhhh</LayoutWrapper> </divpagination not working if i use filtered value and onfilter props in ant table columnfilteredValue={[consultationState]} onFilter={(value:any,record:any)=>{ return recorduseform-metadataBut the error is coming from the UserWithRolesRemove useModal form titleI want to remove title from useModalForm so i gave title ={null } its not workinguseExport with arrays in schemaI have resources that have array fields, which I am spreading to fields like `arrayItem.0.name, arrastartswith operatorstartswith operator not supportedediting usemodal form footeri am having a custom button in antmodal footer footer={<Button >okk</Button>} if i click on this sasetFilters for json objectis there a way to apply nested filters for json b object retuning from usetable , using setFiltersfiltering ant table based on useSelect hookconsider i have select dropdown where i have values as one, two and three . Below i have ant table wselectionSet issueError: unexpected null value for type "uuid": {"response":{"errors":[{"extensions":{"path":"$.selectselection sort issuefield "krseva_frequencies" not found in type: 'query_root': {"response":{"errors":[{"extensions":{"p