微软OAuth实践

原文地址

OAuth有助于开发者轻松获取用户信息,这里我将向你展示微软OAuth在react hook中的用法,让我们开始旅程。

在Azure配置

注册你的APP

提示: 记住 application ID 和 Directory ID 我们后面配置需要用到

准备你的react app

Node modules

我们需要以下npm包

shell 复制代码
npm install @azure/msal-browser
npm install @azure/msal-react

创建一个msconfig.js文件

msconfig.js

js 复制代码
export const msalConfig = {
    auth: {
        clientId: "Enter_the_Application_Id_Here", // application id
        authority: "https://login.microsoftonline.com/Enter_the_Tenant_Info_Here", // directory id
        redirectUri: "http://localhost:3000", // after auth success, will redirect in this uri
    },
    cache: {
        cacheLocation: "sessionStorage", // This configures where your cache will be stored
    },
}

创建一个MsAuthProvider

MsAuthProvider 能够帮助我们在react hook下更好的使用微软的OAuth

MsAuthProvider.jsx

jsx 复制代码
import * from "react";
import { MsalProvider } from '@azure/msal-react';
import { PublicClientApplication } from '@azure/msal-browser';
import { msalConfig } from './msconfig.js';

// create msal instance and export it for using.
export const msalInstance = new PublicClientApplication(msalConfig);

export default function MsAuthProvider({ children }) {
    return <MsalProvider instance={msalInstance}>
        {children}
    </MsalProvider>
}

用 MsAuthProvider 把你的 react App 包裹起来

在App.jsx, 我们需要像下面的代码一样重构一下App。

jsx 复制代码
import * from 'react';
import { AuthenticatedTemplate, UnauthenticatedTemplate } from '@azure/msal-react';
import App from './App.jsx'
import MsAuthProvider from './msAuthProvider/index.jsx';
import SignIn from './pages/signin/index.jsx';

ReactDOM.createRoot(document.getElementById('root')!).render(
    <MsAuthProvider>
        <AuthenticatedTemplate>
          <App />
        </AuthenticatedTemplate>
        <UnauthenticatedTemplate>
          <SignIn />
        </UnauthenticatedTemplate>
    </MsAuthProvider>
)

我们可以使用 @azure/msal-react 提供的 AuthenticatedTemplateUnauthenticatedTemplate,这有助于控制显示组件, 我们不需要控制是否授权的状态, MsAuthProvider 会帮助我们处理这个状态。

创建一个 SignIn component

SignIn component index.jsx

jsx 复制代码
import * from 'react';
import { Card, Typography, CardBody, CardFooter, Button } from "@material-tailwind/react";
import { useSelector } from "react-redux";
import { useMsal } from "@azure/msal-react";

export default function SignIn() {
    const handleLogin = () => {
        // this will open a popup for signin, after singin popup will be close, then
        // app will redirect to the redirectUri as the config in msalConfig
        instance.loginPopup({
            scopes: ["User.Read"]
        }).catch((e) => console.log(e));
    };

    return (
        <div className="flex justify-center mt-32">
            <Card className="w-96">
                <CardBody className="flex flex-col gap-4 place-items-center">
                    <Typography variant="h3">
                        Sign In
                    </Typography>
                </CardBody>
                <CardFooter className="pt-0">
                    <Button
                        className="bg-blue-900"
                        fullWidth
                        onClick={handleLogin}
                    >
                        Sign In With MicroSoft
                    </Button>
                </CardFooter>
            </Card>
        </div>
    )
}

点击登录就会出现以下弹窗

获取用户信息

profile.jsx

jsx 复制代码
import *, { useEffect } from "react";
import { useMsal } from '@azure/msal-react';

export default function Profile() {
    const [profileData, setProfileData] = useState(null);
    const { instance, accounts } = useMsal();
    const getProfile = async () => {
        // get access token by instance, this instance is provided by MsAuthProvider
        const accessToken = await instance.acquireTokenSilent({
            scopes: ["User.Read"]
            account: accounts[0],
        }).then((response) => response.accessToken);
        const headers = new Headers();
        const bearer = `Bearer ${accessToken}`;
        headers.append("Authorization", bearer);
        const options = {
            method: "GET",
            headers: headers
        };
        // fetch user information
        fetch("https://graph.microsoft.com/v1.0/me", options)
            .then(res => res.json())
            .then((res) => setProfileData(res))
    }

    useEffect(() => {
        if (!profileData) {
            getProfile();
        }
    }, [profileData]);

    return (
        <div className="flex flex-col items-center">
            {
                profileData && <div className="flex flex-col items-center">
                    <p>
                        <strong>First Name: </strong> {profileData.givenName}
                    </p>
                    <p>
                        <strong>Last Name: </strong> {profileData.surname}
                    </p>
                    <p>
                        <strong>Email: </strong> {profileData.userPrincipalName}
                    </p>
                    <p>
                        <strong>Id: </strong> {profileData.id}
                    </p>
                </div>
            }
            <button onClick={getProfile}>Get user information</button>
        </div>
    );
};

scopes 参数的 ["User.Read"] 用来获取 access token,['openid'] 用来获取 id token。

集成 Axios

Axios 有 interceptors 能帮助我们在请求之前配置一些请求配置。

request.js

js 复制代码
import axios from 'axios'
import { msalInstance }  from './msAuthProvider/index.jsx';
const request = axios.create({
  baseURL: '/'
})
// add 
request.interceptors.request.use(async config => {
    // msalInstance is exported by msAuthProvider file, because we can not use the hook here
    const accounts = msalInstance.getAllAccounts();
    if (accounts.length > 0) {
        const accessToken = await instance.acquireTokenSilent({
            scopes: ["User.Read"]
            account: accounts[0],
        }).then((response) => response.accessToken);
        if (accessToken) {
            config.headers.Authorization = `Bearer ${accessToken}`
        }
    }
    return config;
}, function (error) {
    return Promise.reject(error);
});

export default request;

然后我们需要重构一下 profile.jsx

jsx 复制代码
import *, { useEffect } from "react";
import { useMsal } from '@azure/msal-react';
import request from './request.js'

export default function Profile() {
    const [profileData, setProfileData] = useState(null);
    const { instance, accounts } = useMsal();
    const getProfile = () => {
        // replce fetch by axios request
        request.get("https://graph.microsoft.com/v1.0/me")
            .then((res) => setProfileData(res))
    }

    useEffect(() => {
        if (!profileData) {
            getProfile();
        }
    }, [profileData]);

    return (
        <div className="flex flex-col items-center">
            {
                profileData && <div className="flex flex-col items-center">
                    <p>
                        <strong>First Name: </strong> {profileData.givenName}
                    </p>
                    <p>
                        <strong>Last Name: </strong> {profileData.surname}
                    </p>
                    <p>
                        <strong>Email: </strong> {profileData.userPrincipalName}
                    </p>
                    <p>
                        <strong>Id: </strong> {profileData.id}
                    </p>
                </div>
            }
            <button onClick={getProfile}>Get user information</button>
        </div>
    );
};

参考链接

  1. Migration Guide from msal v1 to @azure/msal-react and @azure/msal-browser
  2. Tutorial: Register a Single-page application with the Microsoft identity platform
  3. Axios Interceptor
相关推荐
0思必得0几秒前
[Web自动化] Selenium执行JavaScript语句
前端·javascript·爬虫·python·selenium·自动化
程序员敲代码吗15 分钟前
MDN全面接入Deno兼容性数据:现代Web开发的“一张图”方案
前端
0思必得017 分钟前
[Web自动化] Selenium截图
前端·爬虫·python·selenium·自动化
疯子****1 小时前
【无标题】
前端·clawdbot
RichardLau_Cx2 小时前
【保姆级实操】MediaPipe SDK/API 前端项目接入指南(Web版,可直接复制代码)
前端·vue·react·webassembly·mediapipe·手部追踪·前端计算机视觉
不爱写程序的东方不败2 小时前
APP接口测试流程实战Posman+Fiddler
前端·测试工具·fiddler
编码者卢布2 小时前
【Azure App Service】部署在应用服务上的WebJob中,为何会多出一个名为“DaaS“的 WebJob呢?
microsoft·azure
晚霞的不甘3 小时前
Flutter for OpenHarmony构建全功能视差侧滑菜单系统:从动效设计到多页面导航的完整实践
前端·学习·flutter·microsoft·前端框架·交互
黎子越3 小时前
python相关练习
java·前端·python
摘星编程3 小时前
React Native鸿蒙:Tree节点选择状态
react native·react.js·harmonyos