Remix 和 Remix-Auth 的 MicrosoftStrategy
使用Microsoft 策略用于使用 Remix-Auth 对 Microsoft Active Directory 上的帐户进行用户身份验证。这可以是工作/学校帐户或个人 Microsoft 帐户,例如 Skype、Xbox 和 Outlook.com。它扩展了 OAuth2Strategy。
支持的运行时
运行时 | 支持 |
---|---|
Node.js | ✅ |
Cloudflare | ✅ |
使用方法
创建 OAuth 应用程序
请按照 Microsoft 文档 中的步骤创建新的应用程序注册。您应该选择 **Web** 作为平台,配置 **重定向 URI** 并添加客户端密钥。
If you want to support login with both personal Microsoft accounts and school/work accounts, you might need to configure the supported account types by editing the manifest file. Set `signInAudience` value to `MicrosoftADandPersonalMicrosoftAccount` to allow login also with personal accounts.
将您的重定向 URI 更改为 https://example.com/auth/microsoft/callback
或 https://127.0.0.1:4200/auth/microsoft/callback
(如果您在本地运行)。
确保复制客户端密钥、重定向 URI、租户 ID 和应用程序(客户端)ID(在概述中),因为您稍后将需要它们。
安装依赖项
npm install remix-auth-microsoft remix-auth remix-auth-oauth2
创建策略实例
// app/services/auth.server.ts
import { MicrosoftStrategy } from "remix-auth-microsoft";
import { Authenticator } from "remix-auth";
import { sessionStorage } from "~/services/session.server";
export let authenticator = new Authenticator<User>(sessionStorage); //User is a custom user types you can define as you want
let microsoftStrategy = new MicrosoftStrategy(
{
clientId: "YOUR_CLIENT_ID",
clientSecret: "YOUR_CLIENT_SECRET",
redirectUri: "https://example.com/auth/microsoft/callback",
tenantId: "YOUR_TENANT_ID", // optional - necessary for organization without multitenant (see below)
scope: "openid profile email", // optional
prompt: "login", // optional
},
async ({ accessToken, extraParams, profile }) => {
// Here you can fetch the user from database or return a user object based on profile
// return {profile}
// The returned object is stored in the session storage you are using by the authenticator
// If you're using cookieSessionStorage, be aware that cookies have a size limit of 4kb
// For example this won't work
// return {accessToken, extraParams, profile}
// Retrieve or create user using id received from userinfo endpoint
// https://graph.microsoft.com/oidc/userinfo
// DO NOT USE EMAIL ADDRESS TO IDENTIFY USERS
// The email address received from Microsoft Entra ID is not validated and can be changed to anything from Azure Portal.
// If you use the email address to identify users and allow signing in from any tenant (`tenantId` is not set)
// it opens up a possibility of spoofing users!
return User.findOrCreate({ id: profile.id });
}
);
authenticator.use(microsoftStrategy);
有关 scope
和 prompt
参数的更多信息,请参阅 Microsoft 文档。
具有单租户身份验证的应用程序(不允许多租户)
如果您只想允许来自单个组织的用户登录,则应将 tenantId
属性添加到传递给 MicrosoftStrategy
的配置中。tenantId
的值应是您在应用程序注册页面上的 **概述** 中找到的 **目录(租户)ID**。
您还必须在您的应用程序注册中选择 **此组织目录中的帐户** 作为支持的帐户类型。
设置您的路由
// app/routes/login.tsx
export default function Login() {
return (
<form action="/auth/microsoft" method="post">
<button>Login with Microsoft</button>
</form>
);
}
// app/routes/auth/microsoft.tsx
import type { ActionArgs } from "@remix-run/node";
import { authenticator } from "~/auth.server";
import { redirect } from "@remix-run/node";
export const loader = () => redirect("/login");
export const action = ({ request }: ActionArgs) => {
return authenticator.authenticate("microsoft", request);
};
// app/routes/auth/microsoft/callback.tsx
import type { LoaderArgs } from "@remix-run/node";
import { authenticator } from "~/auth.server";
export const loader = ({ request }: LoaderArgs) => {
return authenticator.authenticate("microsoft", request, {
successRedirect: "/dashboard",
failureRedirect: "/login",
});
};
添加会话存储
// app/services/session.server.ts
import { createCookieSessionStorage } from "@remix-run/node";
export let sessionStorage = createCookieSessionStorage({
cookie: {
name: "_session", // use any name you want here
sameSite: "lax", // this helps with CSRF
path: "/", // remember to add this so the cookie will work in all routes
httpOnly: true, // for security reasons, make this cookie http only
secrets: ["s3cr3t"], // replace this with an actual secret
secure: process.env.NODE_ENV === "production", // enable this in prod only
},
});
export let { getSession, commitSession, destroySession } = sessionStorage;