前端对接微软AAD登录并使用.net webapi验证微软aad token

作者 : admin 本文共5394个字,预计阅读时间需要14分钟 发布时间: 2024-05-29 共1人阅读

大致流程

1.添加应用注册,获取应用程序id,租户id,添加回调地址,添加graphapi权限,添加范围,生成验证字符串
2.跳转微软登录并从回调地址获取code
3.用code获取token
4.验证token

一.前期准备

1.在Microsoft Entra ID(AAD)中新建一个应用注册,或使用之前有的也行
前端对接微软AAD登录并使用.net webapi验证微软aad token插图

2.进入应用注册后,记录下应用程序ID(ClientID)和租户ID(TenantID)
前端对接微软AAD登录并使用.net webapi验证微软aad token插图(1)
3.在API权限除添加User.Read委托权限,这个权限分类为Microsoft Graph
前端对接微软AAD登录并使用.net webapi验证微软aad token插图(2)
4.在公开API处保存应用程序IDURI,并新建一个范围,命名为User.Read,并记录范围全程:格式为api://{ClientID}/{范围},这个是后续的scope参数,必须为应用注册的自定义范围才行,要使用graph的scope,获取到的token就是graph的token,而不是这个应用注册的token,会导致验证token失败
前端对接微软AAD登录并使用.net webapi验证微软aad token插图(3)
5.配置回调地址
在身份验证处添加你想要的回调地址,后续获取code时会拼在你指定的回调地址后,比如http://localhost:8099/app.html?code={code},回调地址中不能有#号,会在获取token时匹配失败!
前端对接微软AAD登录并使用.net webapi验证微软aad token插图(4)

6.生成证明密钥和验证字符串

string codeVerifier = PkceHelper.GenerateCodeVerifier();//验证字符串生成(后续获取token使用)
string codeChallenge = PkceHelper.GenerateCodeChallenge(codeVerifier);//证明密钥(获取Code使用)

public class PkceHelper
{
    public static string GenerateCodeVerifier()
    {
        using (var rng = new RNGCryptoServiceProvider())
        {
            var byteArray = new byte[32];
            rng.GetBytes(byteArray);
            return Convert.ToBase64String(byteArray)
                .TrimEnd('=')
                .Replace('+', '-')
                .Replace('/', '_');
        }
    }

    public static string GenerateCodeChallenge(string codeVerifier)
    {
        using (var sha256 = SHA256.Create())
        {
            var challengeBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(codeVerifier));
            return Convert.ToBase64String(challengeBytes)
                .TrimEnd('=')
                .Replace('+', '-')
                .Replace('/', '_');
        }
    }
}

二.前端获取code

这一步也很简单,就是把上面所有获取到的参数拼成指向微软的url,跳转并登录后,拿到回调地址后面的code

let tenant = '11111111-2222-3333-4444-555553957ae4';//租户id
let client_id = '22211111-2222-3333-4444-555553957ae4';//应用程序id
let redirect_uri = 'http%3A%2F%2Flocalhost:8099%2Fapi.html';//uri转码后回调地址
let scope = 'api://22211111-2222-3333-4444-555553957ae4/User.Read';//范围
let response_type = 'code';//默认code
let code_challenge = 'testtesttest';//证明密钥
let state = 'test';//自定义状态
let url = 'https://login.microsoftonline.com/'+tenant+'/oauth2/v2.0/authorize?client_id='+client_id+'&response_type='+response_type+'&scope='+scope+'&redirect_uri='+redirect_uri+'&response_mode=query&state='+state+'&code_challenge='+code_challenge+'&code_challenge_method=S256';//拼接url

跳转这个链接,登录成功后就会回调到配置的地址,就可获取到code
前端对接微软AAD登录并使用.net webapi验证微软aad token插图(5)

三.前端获取Token

注意一个code只能使用一次

const tenantId = '11111111-2222-3333-4444-555553957ae4';//租户id
const clientId = '22211111-2222-3333-4444-555553957ae4';//应用程序id
const redirectUri = 'http://localhost:8099/api.html';//回调地址,因为是接口请求,所以不用转码
const authorizationCode = '0.AT4AyT91testestlkcwZ0BDnf3Op-z2YZYAIc.AgABBAIAAADnfolhJpSnRYB1SVj-Htesttestetestet_6xfpglv1V'; //填入上一步的code,要注意别把state或其他参数跟在后面
const codeVerifier = 'test1test1'; //验证字符串,第一步第6点生成的
const tokenEndpoint = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;//请求路径

//请求
const body = new URLSearchParams({
	client_id: clientId,
	grant_type: 'authorization_code',
	code: authorizationCode,
	redirect_uri: redirectUri,
	code_verifier: codeVerifier // 如果使用 PKCE
});
try
{
    const response = await fetch(tokenEndpoint, {
    method: 'POST',
    headers:
        {
            'Content-Type': 'application/x-www-form-urlencoded'
                    },
                    body: body.toString()
         });

    if (!response.ok)
    {
        throw new Error(`HTTP error! status: ${ response.status }`);
    }

    const data = await response.json();
    console.log('Access Token:', data.access_token);//打印Token
}
catch (error)
{
    console.error('Error fetching token:', error);
}

因为我主要写后端的,所以我这里写的前端只涉及到拼接url和调用微软接口,具体页面和回调后获取token的后续操作可以自定义实现,不过大致也是进入web系统后,先判断本地存储里有没有token,有的话判断一下token是否过期,没有或者过期则拼接微软url,跳转登录后,从回调地址获取到code,再用code获取token,之后发给后端验证。

四.后端验证token

这里用的.net webapi来进行验证,因为微软的token有效期只有一个多小时,我的想法是用微软token去兑换自定义token,这样就不用频繁调用微软api来刷新或者重新登录获取token

验证微软token
注释里有些提示很重要

/// 
/// 验证微软Token
/// 
/// 
public async Task<bool> AuthMicToken(string token)
{
//下方都是配置项
string azureAdInstance = configuration["AzureAd:Instance"];//实例,一般是https://login.microsoftonline.com/,具体可以看token解析后的iss字段里租户id前的域名
string validIssuer = configuration["AzureAd:ValidIssuer"];//颁发者,一般是https://login.microsoftonline.com/{tenantId }/v2.0 这个格式,具体可以看token解析后的iss字段,验证不成功可能是这个字段不匹配,需要和token的iss完全一致
string tenantId = configuration["AzureAd:TenantId"];//租户id
string audience = configuration["AzureAd:ClientId"];//接收者,一般是应用程序id,具体可以看token解析后的aud字段,验证不成功可能是这个字段不匹配,需要和token的aud完全一致
string emailField = configuration["AzureAd:EmailField"];//业务要求获取token里邮箱对应字段
validIssuer = string.Format(validIssuer, tenantId);
if (token.StartsWith("Bearer "))
{
token = token.Substring("Bearer ".Length).Trim();
}
//定义验证参数
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidAudiences = new List<string>(){ audience },
ValidIssuer = validIssuer,
IssuerSigningKeys = GetSigningKeys(azureAdInstance, tenantId),
};
try
{
//这里验证token,验证失败就会抛异常
var principal = tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken);
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "验证微软token失败");
return false;
}
}
//获取密钥
public static IEnumerable<SecurityKey> GetSigningKeys(string azureAdInstance, string tenantId)
{
// Retrieve the public keys from the OpenID Connect metadata endpoint
string openIdConnectMetadataUrl = $"{azureAdInstance}{tenantId}/v2.0/.well-known/openid-configuration";
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(openIdConnectMetadataUrl, new OpenIdConnectConfigurationRetriever());
OpenIdConnectConfiguration openIdConfig = configurationManager.GetConfigurationAsync().Result;
return openIdConfig.SigningKeys;
}

这个站点可以解析token:https://jwt.io/
token解析示例:
前端对接微软AAD登录并使用.net webapi验证微软aad token插图(6)

参考网站

微软官方文档
https://learn.microsoft.com/zh-cn/entra/identity-platform/v2-oauth2-auth-code-flow
前端对接微软AAD登录并使用.net webapi验证微软aad token插图(7)

踩雷

验证token失败: IDX10511,原因是scope没有使用自定义范围
https://stackoverflow.com/questions/69589324/securitytokeninvalidsignatureexception-idx10511
获取code失败:需要添加验证字符串
获取token失败:报需要跨域时,不能使用api调用软件,postman等
验证token失败:验证者和颁发者必须完全匹配!
验证token失败:token解析后aud为 “00000003-0000-0000-c000-000000000000″时为graph的token,必须scope使用自定义范围

本站无任何商业行为
个人在线分享 » 前端对接微软AAD登录并使用.net webapi验证微软aad token
E-->