Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
ra-auth-msal
Advanced tools
An auth provider for [react-admin](https://github.com/marmelab/react-admin) that handles authentication using the [Microsoft Authentication Library (MSAL)](https://learn.microsoft.com/fr-fr/azure/active-directory/develop/msal-overview).
An auth provider for react-admin that handles authentication using the Microsoft Authentication Library (MSAL).
This is useful when using Azure Active Directory to authenticate your users.
This package provides:
msalAuthProvider
function to get the auth providermsalHttpClient
helper to get a fetch
-like function that adds the access token to the requestLoginPage
component that displays a loading indicator if the redirection takes too longyarn add ra-auth-msal
# or
npm install --save ra-auth-msal
// in src/authConfig.js
export const msalConfig = {
auth: {
// 'Application (client) ID' of app registration in Azure portal - this value is a GUID
clientId: "12345678-1234-1234-1234-123456789012",
// Full directory URL, in the form of https://login.microsoftonline.com/<tenant-id>
authority: "https://login.microsoftonline.com/common",
// Full redirect URL, in form of http://localhost:8080/auth-callback
redirectUri: "http://localhost:8080/auth-callback",
// We need to disable this feature because it is already handled by react-admin, and would otherwise conflict
navigateToLoginRequestUrl: false,
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false,
},
};
// in src/App.jsx
import React, { useEffect } from "react";
import { Admin, Resource } from 'react-admin';
import { BrowserRouter } from "react-router-dom";
import { PublicClientApplication } from "@azure/msal-browser";
import { LoginPage, msalAuthProvider } from "ra-auth-msal";
import dataProvider from './dataProvider';
import posts from './posts';
import { msalConfig } from "./authConfig";
const myMSALObj = new PublicClientApplication(msalConfig);
const App = () => {
const [isMSALInitialized, setMSALInitialized] = React.useState(false);
useEffect(() => {
myMSALObj.initialize().then(() => {
setMSALInitialized(true);
});
}, []);
const authProvider = msalAuthProvider({
msalInstance: myMSALObj,
});
if (!isMSALInitialized) {
return <div>Loading...</div>;
}
return (
<BrowserRouter>
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
</BrowserRouter>
);
};
export default App;
Tip: You need to wrap your <Admin>
component in a <BrowserRouter>
for this library to work. Indeed, MSAL uses a hash-based routing strategy when redirecting back to your app, which is incompatible with a <HashRouter>
.
// in src/authConfig.js
export const msalConfig = {
// ...
};
/**
* Customize this map to match your own roles and permissions
*/
const rolesPermissionMap = {
"12345678-1234-1234-1234-123456789012": "user",
"12345678-1234-1234-1234-123456789013": "admin",
};
/**
* Custom function to map roles to permissions, using the rolesPermissionMap above.
* Alternatively, you can use the MS Graph API to get more information about the user's roles and groups.
*/
export const getPermissionsFromAccount = async (account) => {
const roles = account?.idTokenClaims?.roles ?? [];
return roles.map((role) => rolesPermissionMap[role]);
};
// in src/App.jsx
import React, { useEffect } from "react";
import { Admin, Resource } from 'react-admin';
import { BrowserRouter } from "react-router-dom";
import { PublicClientApplication } from "@azure/msal-browser";
import { LoginPage, msalAuthProvider } from "ra-auth-msal";
import dataProvider from './dataProvider';
import posts from './posts';
import { msalConfig, getPermissionsFromAccount } from "./authConfig";
const myMSALObj = new PublicClientApplication(msalConfig);
const App = () => {
const [isMSALInitialized, setMSALInitialized] = React.useState(false);
useEffect(() => {
myMSALObj.initialize().then(() => {
setMSALInitialized(true);
});
}, []);
const authProvider = msalAuthProvider({
msalInstance: myMSALObj,
getPermissionsFromAccount,
});
if (!isMSALInitialized) {
return <div>Loading...</div>;
}
return (
<BrowserRouter>
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
</BrowserRouter>
);
};
export default App;
// in src/authConfig.js
export const msalConfig = {
// ...
};
/**
* Custom function to get the identity from the account info.
*/
export const getIdentityFromAccount = async (account) => {
return {
id: account?.localAccountId,
fullName: account?.username,
};
};
// in src/App.jsx
import React, { useEffect } from "react";
import { Admin, Resource } from 'react-admin';
import { BrowserRouter } from "react-router-dom";
import { PublicClientApplication } from "@azure/msal-browser";
import { LoginPage, msalAuthProvider } from "ra-auth-msal";
import dataProvider from './dataProvider';
import posts from './posts';
import { msalConfig, getIdentityFromAccount } from "./authConfig";
const myMSALObj = new PublicClientApplication(msalConfig);
const App = () => {
const [isMSALInitialized, setMSALInitialized] = React.useState(false);
useEffect(() => {
myMSALObj.initialize().then(() => {
setMSALInitialized(true);
});
}, []);
const authProvider = msalAuthProvider({
msalInstance: myMSALObj,
getIdentityFromAccount,
});
if (!isMSALInitialized) {
return <div>Loading...</div>;
}
return (
<BrowserRouter>
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
</BrowserRouter>
);
};
export default App;
// in src/authConfig.js
export const msalConfig = {
// ...
};
/**
* Scopes you add here will be prompted for user consent during sign-in.
* By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
* For more information about OIDC scopes, visit:
* https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
*/
export const loginRequest = {
scopes: ["User.Read"],
};
// in src/App.jsx
import React, { useEffect } from "react";
import { Admin, Resource } from 'react-admin';
import { BrowserRouter } from "react-router-dom";
import { PublicClientApplication } from "@azure/msal-browser";
import { LoginPage, msalAuthProvider } from "ra-auth-msal";
import dataProvider from './dataProvider';
import posts from './posts';
import { msalConfig, loginRequest } from "./authConfig";
const myMSALObj = new PublicClientApplication(msalConfig);
const App = () => {
const [isMSALInitialized, setMSALInitialized] = React.useState(false);
useEffect(() => {
myMSALObj.initialize().then(() => {
setMSALInitialized(true);
});
}, []);
const authProvider = msalAuthProvider({
msalInstance: myMSALObj,
loginRequest,
});
if (!isMSALInitialized) {
return <div>Loading...</div>;
}
return (
<BrowserRouter>
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
</BrowserRouter>
);
};
export default App;
// in src/authConfig.js
export const msalConfig = {
// ...
};
/**
* Add here the scopes to request when obtaining an access token for MS Graph API. For more information, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
*/
export const tokenRequest = {
scopes: ["User.Read"],
forceRefresh: false, // Set this to "true" to skip a cached token and go to the server to get a new token
};
// in src/App.jsx
import React, { useEffect } from "react";
import { Admin, Resource } from 'react-admin';
import { BrowserRouter } from "react-router-dom";
import { PublicClientApplication } from "@azure/msal-browser";
import { LoginPage, msalAuthProvider } from "ra-auth-msal";
import dataProvider from './dataProvider';
import posts from './posts';
import { msalConfig, tokenRequest } from "./authConfig";
const myMSALObj = new PublicClientApplication(msalConfig);
const App = () => {
const [isMSALInitialized, setMSALInitialized] = React.useState(false);
useEffect(() => {
myMSALObj.initialize().then(() => {
setMSALInitialized(true);
});
}, []);
const authProvider = msalAuthProvider({
msalInstance: myMSALObj,
tokenRequest,
});
if (!isMSALInitialized) {
return <div>Loading...</div>;
}
return (
<BrowserRouter>
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
</BrowserRouter>
);
};
export default App;
redirectOnCheckAuth
You can choose whether the authProvider should redirect to the MS login form when the user is not authenticated. By default, it is set to true
.
It can be useful to set it to false
when you want to trigger the redirection only from a custom login page.
// in src/authConfig.js
export const msalConfig = {
// ...
};
// in src/CustomLoginPage.jsx
import * as React from "react";
import { Button } from "@mui/material";
import { useLogin } from "react-admin";
/**
* Csutom Login Page used to trigger the redirection to the MS login page.
*/
export const CustomLoginPage = () => {
const login = useLogin();
return (
<div>
<Button onClick={() => login({})}>
Sign in with Microsoft
</Button>
</div>
);
};
// in src/App.jsx
import React, { useEffect } from "react";
import { Admin, Resource } from 'react-admin';
import { BrowserRouter } from "react-router-dom";
import { PublicClientApplication } from "@azure/msal-browser";
import { msalAuthProvider } from "ra-auth-msal";
import { CustomLoginPage } from "./CustomLoginPage";
import dataProvider from './dataProvider';
import posts from './posts';
import { msalConfig } from "./authConfig";
const myMSALObj = new PublicClientApplication(msalConfig);
const App = () => {
const [isMSALInitialized, setMSALInitialized] = React.useState(false);
useEffect(() => {
myMSALObj.initialize().then(() => {
setMSALInitialized(true);
});
}, []);
const authProvider = msalAuthProvider({
msalInstance: myMSALObj,
redirectOnCheckAuth: false,
});
if (!isMSALInitialized) {
return <div>Loading...</div>;
}
return (
<BrowserRouter>
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={CustomLoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
</BrowserRouter>
);
};
export default App;
enableDeepLinkRedirect
You can choose whether the authProvider should redirect to the page the user was visiting once the user has been authenticated (this allows easier URL sharing between users). By default, it is set to true
.
Note: This features relies on sessionStorage
and is not available with Server-Side Rendering.
You can disable this feature like this:
// in src/authConfig.js
export const msalConfig = {
// ...
};
// in src/App.jsx
import React, { useEffect } from "react";
import { Admin, Resource } from 'react-admin';
import { BrowserRouter } from "react-router-dom";
import { PublicClientApplication } from "@azure/msal-browser";
import { msalAuthProvider } from "ra-auth-msal";
import { CustomLoginPage } from "./CustomLoginPage";
import dataProvider from './dataProvider';
import posts from './posts';
import { msalConfig } from "./authConfig";
const myMSALObj = new PublicClientApplication(msalConfig);
const App = () => {
const [isMSALInitialized, setMSALInitialized] = React.useState(false);
useEffect(() => {
myMSALObj.initialize().then(() => {
setMSALInitialized(true);
});
}, []);
const authProvider = msalAuthProvider({
msalInstance: myMSALObj,
enableDeepLinkRedirect: false,
});
if (!isMSALInitialized) {
return <div>Loading...</div>;
}
return (
<BrowserRouter>
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
>
<Resource name="posts" {...posts} />
</Admin>
</BrowserRouter>
);
};
export default App;
msalHttpClient
ra-auth-msal
includes an msalHttpClient
that can be used to make authenticated requests to your API. This helper automatically adds the accessToken
to the request headers.
Here is an example with ra-data-json-server
:
// in src/authConfig.js
export const msalConfig = {
// ...
};
/**
* Add here the scopes to request when obtaining an access token for MS Graph API. For more information, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
*/
export const tokenRequest = {
scopes: ["User.Read"],
forceRefresh: false, // Set this to "true" to skip a cached token and go to the server to get a new token
};
// in src/App.jsx
import React, { useEffect } from "react";
import { Admin, Resource } from 'react-admin';
import { BrowserRouter } from "react-router-dom";
import { PublicClientApplication } from "@azure/msal-browser";
import { LoginPage, msalAuthProvider, msalHttpClient } from "ra-auth-msal";
import jsonServerProvider from "ra-data-json-server";
import posts from './posts';
import { msalConfig, tokenRequest } from "./authConfig";
const myMSALObj = new PublicClientApplication(msalConfig);
const App = () => {
const [isMSALInitialized, setMSALInitialized] = React.useState(false);
useEffect(() => {
myMSALObj.initialize().then(() => {
setMSALInitialized(true);
});
}, []);
const authProvider = msalAuthProvider({
msalInstance: myMSALObj,
tokenRequest
});
const httpClient = msalHttpClient({
msalInstance: myMSALObj,
tokenRequest
});
const dataProvider = jsonServerProvider(
"https://jsonplaceholder.typicode.com",
httpClient
);
if (!isMSALInitialized) {
return <div>Loading...</div>;
}
return (
<BrowserRouter>
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
</BrowserRouter>
);
};
export default App;
Tip: By default msalHttpClient
will use the accessToken
. If you want to use the idToken
instead, you will need to provide your own httpClient
like so:
import { fetchUtils } from "react-admin";
const myHttpClient = ({ msalInstance, tokenRequest }) => async (url, options = {}) => {
const account = msalInstance.getActiveAccount();
const authResult = await msalInstance.acquireTokenSilent({
account,
...tokenRequest,
});
const token = authResult && authResult.idToken;
const user = { authenticated: !!token, token: `Bearer ${token}` };
return fetchUtils.fetchJson(url, { ...options, user });
};
refreshAuth
The authProvider already supports automatic refresh of the token. However, if your dataProvider passes the token to your API, you should wrap it with addRefreshAuthToDataProvider
to ensure it also refreshes the token when needed:
import { addRefreshAuthToDataProvider } from 'react-admin';
import { msalRefreshAuth } from "ra-auth-msal";
import { PublicClientApplication } from "@azure/msal-browser";
import { msalConfig, tokenRequest } from "./authConfig";
import { dataProvider } from './dataProvider';
const myMSALObj = new PublicClientApplication(msalConfig);
const dataProvider = addRefreshAuthToDataProvider(
dataProvider,
msalRefreshAuth({
msalInstance: myMSALObj,
tokenRequest,
})
);
You can find a working demo, along with the source code, in this project's repository: https://github.com/marmelab/ra-auth-msal
This auth provider is licensed under the MIT License and sponsored by marmelab.
3.0.2
FAQs
An auth provider for [react-admin](https://github.com/marmelab/react-admin) that handles authentication using the [Microsoft Authentication Library (MSAL)](https://learn.microsoft.com/fr-fr/azure/active-directory/develop/msal-overview).
The npm package ra-auth-msal receives a total of 828 weekly downloads. As such, ra-auth-msal popularity was classified as not popular.
We found that ra-auth-msal demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.