You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过MSAL认证与Azure Functions验证用户邮箱并获取全名

解决方案:验证租户内有效用户并获取全名

核心方案

通过Microsoft Graph API + On-Behalf-Of (OBO)流程实现:后端Azure Function使用前端传来的用户访问令牌,向Azure AD申请Graph API的访问权限,再调用Graph的/users端点根据邮箱查询用户信息——既验证邮箱是否为租户内有效用户,又能直接获取用户全名。

前提准备

  1. 确保你的Azure AD应用注册已授予委托权限user.ReadBasic.All,并完成管理员同意(因为需要查询租户内其他用户)。
  2. 在Azure Function中安装依赖:
    npm install @azure/msal-node @microsoft/microsoft-graph-client isomorphic-fetch
    

修改后的Azure Function代码

const { ConfidentialClientApplication } = require('@azure/msal-node');
const { Client } = require('@microsoft/microsoft-graph-client');
const { TokenCredentialAuthenticationProvider } = require('@microsoft/microsoft-graph-client/authProviders/azureTokenCredentials');
const fetch = require('isomorphic-fetch');

// 配置MSAL客户端(对应Azure AD应用注册的后端应用信息)
const msalConfig = {
    auth: {
        clientId: "YOUR_BACKEND_APP_CLIENT_ID",
        clientSecret: "YOUR_BACKEND_APP_CLIENT_SECRET",
        authority: "https://login.microsoftonline.com/YOUR_TENANT_ID"
    }
};

const cca = new ConfidentialClientApplication(msalConfig);
const tenantId = "YOUR_TENANT_ID";

module.exports = async function (context, req) {
    if (req.method === 'POST') {
        try {
            const { email } = req.body;
            const token = req.headers.authorization;

            if (!token || !email) {
                context.res = {
                    status: 400,
                    body: JSON.stringify({ error: "Token in 'Authorization' header and email in request body are required." })
                };
                return;
            }
            const bearerToken = token.split(' ')[1];

            // 1. OBO流程获取Graph API的访问令牌
            const oboRequest = {
                oboAssertion: bearerToken,
                scopes: ["https://graph.microsoft.com/user.ReadBasic.All"],
            };

            const oboResponse = await cca.acquireTokenOnBehalfOf(oboRequest);
            if (!oboResponse || !oboResponse.accessToken) {
                context.res = {
                    status: 403,
                    body: JSON.stringify({ error: 'Failed to acquire Graph API token' })
                };
                return;
            }

            // 2. 调用Graph API查询用户
            const graphClient = Client.initWithMiddleware({
                authProvider: new TokenCredentialAuthenticationProvider({
                    getToken: async () => ({ token: oboResponse.accessToken, expiresOnTimestamp: Date.now() + oboResponse.expiresIn * 1000 })
                }),
                fetchProvider: () => fetch
            });

            try {
                // 根据邮箱查询用户,$select指定需要的字段
                const user = await graphClient.api(`/users/${encodeURIComponent(email)}`).select('displayName,mail').get();
                
                // 返回验证成功的信息及用户全名
                context.res = {
                    status: 200,
                    body: JSON.stringify({ 
                        message: 'Email is a valid tenant user.',
                        fullName: user.displayName,
                        userEmail: user.mail
                    })
                };
            } catch (graphError) {
                // Graph返回404表示用户不存在
                if (graphError.statusCode === 404) {
                    context.res = {
                        status: 400,
                        body: JSON.stringify({ error: 'Email is not a valid user in the tenant.' })
                    };
                } else {
                    context.log.error('Graph API error:', graphError.message);
                    context.res = {
                        status: 500,
                        body: JSON.stringify({ error: 'Failed to verify user via Graph API' })
                    };
                }
            }

        } catch (error) {
            context.log.error('Token verification error:', error.message);
            context.res = {
                status: 401,
                body: JSON.stringify({ error: 'Invalid token' })
            };
        }
    } else {
        context.res = {
            status: 405,
            body: JSON.stringify({ error: "Method Not Allowed" })
        };
    }
};

关键说明

  • OBO流程:后端用用户的访问令牌换取Graph API的专用令牌,确保调用Graph的权限是基于当前登录用户的委托权限。
  • Graph API查询:使用/users/{email}端点精准查询,若返回404则判定为无效用户,200则提取displayName作为全名。
  • 权限保障:user.ReadBasic.All委托权限允许登录用户查询租户内所有用户的基本信息,完全覆盖需求场景。

内容的提问来源于stack exchange,提问作者Snooper_A

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:42:50