Azure AD认证和Azure AD B2C的token获取
创始人
2024-01-26 09:51:44
0

Azure AD认证和Azure AD B2C的token获取


工作当中使用过Azure AD认证和B2C的认证,今天抽时间再回顾一下。

个人理解比较浅显,我认为Azure AD和Azure AD B2C都可作为用户管理的系统,他们提供了自己的登录认证画面,统一使用Graph API对自己的用户和其他功能做管理。

Azure AD功能强大,微软的老牌认证方式,可以很方便跟其他三方应用集成,可做单点登录。
而Azure AD B2C更像是三方的用户系统,最大的特点是可自定义UI画面。

感觉总结的不是很好,纯纯自己的理解,这里就不多说了,让我们进入正题。

这里主要贴一下,当时使用的认证相关获取token的代码。

一、Azure AD

1、获取认证登录地址
下面的代码是nodejs的示例,需要用到msal-node库·,当然可以根据官网自己拼接URL也是可以的,在之前曾经尝试过,没有任何问题。
只是在当时不知道为什么,老是提示我需要change_codeverifier两个参数,因为不明白这两个参数的原理,所以采用了msal-node库去生成。
msal参考地址: https://azuread.github.io/microsoft-authentication-library-for-js/ref/classes/_azure_msal_browser.publicclientapplication.html

const msal = require("@azure/msal-node");function getAuthCodeUrl (){return new Promise((resolve, reject) => {const cryptoProvider = new msal.CryptoProvider();cryptoProvider.generatePkceCodes().then(({ verifier, challenge }) => {const publiClient= new msal.PublicClientApplication({auth: {"clientId": SETTINGS.AD_CLIENT_ID,"authority": `https://login.microsoftonline.com/${SETTINGS.AD_TENANT_ID}`}});publiClient.getAuthCodeUrl({scopes: ["user.read"],redirectUri: SETTINGS.REDIRECT_URL,codeChallenge: challenge,codeChallengeMethod: "S256"}).then((response) => {let result = {authUrl: response,verifier: verifier,};resolve(result);}).catch((error) => {reject(JSON.stringify(error));});});});
}

2、access_token的获取
① 通过认证code获取access_token。
在通过认证地址登录后,会跳转到重定向地址,附带client_infocode参数,verifier参数由1、中获取。
官方参考地址: https://learn.microsoft.com/zh-cn/graph/auth-v2-user

const rp = require("request-promise");function getADToken (code, clientInfo, verifier) {return new Promise((resolve, reject) => {let options = {method: "POST",uri: `https://login.microsoftonline.com/${SETTINGS.AD_TENANT_ID}/oauth2/v2.0/token`,form: {"client_id": SETTINGS.AD_CLIENT_ID,"scope": "openid offline_access profile","grant_type": "authorization_code","redirect_uri": SETTINGS.REDIRECT_URL,"code": code,"client_secret": SETTINGS.AD_SECRIT_ID,"client_info": clientInfo,"code_verifier": verifier,},headers: {"Content-Type": "application/x-www-form-urlencoded",},};rp(options).then((res) => {resolve(JSON.parse(res)["access_token"]);}).catch((err) => {reject(err);});});
}

二、Azure AD B2C

1、获取认证登录地址

// B2C配置对象const b2cConfig= {auth: {clientId: SETTINGS.B2C_CLIENT_ID,authority: `https://${SETTINGS.B2C_TENANT_NAME}.b2clogin.com/${SETTINGS.B2C_TENANT_NAME}.onmicrosoft.com/${SETTINGS.B2C_POLICY}`,knownAuthorities: [`${SETTINGS.B2C_TENANT_NAME}.b2clogin.com`],redirectUri: SETTINGS.REDIRECT_URL,}};// 创建msal对象
const publicClient = new msal.PublicClientApplication(b2cConfig);function getAuthCodeUrl (){return new Promise((resolve, reject) => {const cryptoProvider = new msal.CryptoProvider();cryptoProvider.generatePkceCodes().then(({ verifier, challenge }) => {publicClient.getAuthCodeUrl({redirectUri: b2cConfig.auth.redirectUri,authority: b2cConfig.auth.authority,scopes: ["openid", "offline_access"],state: "login",codeChallenge: challenge,codeChallengeMethod: "S256",}).then((response) => {let result = {authUrl: response,verifier: verifier,};resolve(result);}).catch((error) => {reject(JSON.stringify(error));});});});
}

2、获取IdToken
B2C认证拿到的code只能换取idToken, 就算拿到了access_token也是属于web_token,不能用于调用graph api。这是跟微软support确认后的结果。

function getIdToken (code, codeVerifier) {return new Promise((resolve, reject) => {// prepare the request for authenticationconst tokenRequest = {redirectUri: b2cConfig.auth.redirectUri,code: code,codeVerifier: codeVerifier,};pca.acquireTokenByCode(tokenRequest).then((response) => {resolve(response);}).catch((error) => {reject(error);});});
}

3、解析IdToken
IdToken中包含用户的相关信息,但是没法查看,需要用到jwt-decode库解析。

const jwtDecode = require("jwt-decode");
let tokenObj = jwtDecode(idToken);

4、获取access_token
B2C不能使用认证code获取access_token,所以采用了Azure AD免登录的方式获取了access_token。
官方参考地址: https://learn.microsoft.com/zh-cn/graph/auth-v2-service

const confidentialClientPca = new msal.ConfidentialClientApplication({auth: {"clientId": SETTINGS.B2C_CLIENT_ID,"authority": `https://login.microsoftonline.com/${SETTINGS.B2C_TENANT_ID}`,"clientSecret": SETTINGS.B2C_SECRIT_ID,}});function getClientCredentialsToken () {return new Promise((resolve, reject) => {const clientCredentialRequest = {scopes: ["https://graph.microsoft.com/.default"],azureRegion: null, // (optional) specify the region you will deploy your application to here (e.g. "westus2")skipCache: false, // (optional) this skips the cache and forces MSAL to get a new token from Azure AD};confidentialClientPca.acquireTokenByClientCredential(clientCredentialRequest).then((response) => {resolve(response.accessToken);}).catch((error) => {reject(JSON.stringify(error));});});
}

三、C# 操作示例

依赖包

using System;
using System.Collections.Generic;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System.IO;namespace AZ.Functuon
{public class GraphAPI{public GraphServiceClient client = null;public StreamWriter logWriter;public string tenantName;public GraphAPI(string tenantId,string tenantName,string clientId,string clientSecret,StreamWriter logWriter){try{this.tenantName = tenantName;this.logWriter = logWriter;string[] scopes = new[] { "https://graph.microsoft.com/.default" };IConfidentialClientApplication cca = ConfidentialClientApplicationBuilder.Create(clientId).WithTenantId(tenantId).WithClientSecret(clientSecret).Build();Task taskResult = cca.AcquireTokenForClient(scopes).ExecuteAsync();taskResult.Wait();InitClint(cca, scopes);}catch (Exception e){CommonFunction.WriteLog(logWriter, "ERROR", $"{e.Message} | {e.StackTrace}");this.client = null;}}public void InitClint(IConfidentialClientApplication cca,string[] scopes){DelegateAuthenticationProvider authProvider = new DelegateAuthenticationProvider(async (request) =>{AuthenticationResult result = await cca.AcquireTokenForClient(scopes).ExecuteAsync();request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);});this.client = new GraphServiceClient(authProvider);}// ユーザのObjectIdを取得するpublic async Task GetObjectId(string employeeId){try{var result = await this.client.Users.Request().Filter($"identities/any(c:c/issuerAssignedId eq '{employeeId}' and c/issuer eq '{this.tenantName}@onmicrosoft.com')").Select(e => new{e.DisplayName,e.Id,e.Identities}).GetAsync();if (result != null){JObject jo = (JObject)JsonConvert.DeserializeObject(System.Text.Json.JsonSerializer.Serialize(result).Substring(1, System.Text.Json.JsonSerializer.Serialize(result).Length-2));return jo["id"].ToString();}else{return null;}}catch(Exception e){CommonFunction.WriteLog(logWriter, "ERROR", $"{e.Message} | {e.StackTrace}");return null;}}// ユーザのObjectIdをtest取得するpublic async Task GetObjectIdByUserprincipalname(string userPrincipalName){try{String [] userPrincipalNamefirst=userPrincipalName.Split("@");var result = await this.client.Users[$"{userPrincipalNamefirst[0]}@{this.tenantName}.onmicrosoft.com"].Request(new Option[] { new QueryOption("$count", "true")}).Header("ConsistencyLevel", "eventual").GetAsync();if (result != null){JObject jo = (JObject)JsonConvert.DeserializeObject(System.Text.Json.JsonSerializer.Serialize(result));return jo["id"].ToString();}else{return null;}}catch(Exception e){CommonFunction.WriteLog(logWriter, "ERROR", $"{e.Message} | {e.StackTrace}");return null;}}// B2Cにユーザを新規追加するpublic async Task CreateB2CUser(string employeeId, string userName,string userPrincipalName, string birthday){try{userPrincipalName = userPrincipalName.Split("@")[0];var user = new User{AccountEnabled = true,DisplayName = userName,MailNickname = userPrincipalName,UserPrincipalName = $"{userPrincipalName}@{this.tenantName}.onmicrosoft.com",PasswordProfile = new PasswordProfile{ForceChangePasswordNextSignIn = false,Password = $"Fj_{birthday}"},Identities = new List(){new ObjectIdentity{SignInType = "userName",Issuer = "{this.tenantName}.onmicrosoft.com",IssuerAssignedId = employeeId},}};var result = await this.client.Users.Request().AddAsync(user);return true;}catch(Exception e){CommonFunction.WriteLog(logWriter, "ERROR", $"{e.Message} | {e.StackTrace}");return false;}}// B2Cユーザの認証電話番号追加public async Task AddAuthPhoneNumber(string objectID, string phone){try{var phoneAuthenticationMethod = new PhoneAuthenticationMethod{PhoneNumber = $"+81 {phone}",PhoneType = AuthenticationPhoneType.Mobile};await this.client.Users[objectID].Authentication.PhoneMethods.Request().AddAsync(phoneAuthenticationMethod);return true;}catch(Exception e){CommonFunction.WriteLog(logWriter, "ERROR", $"{e.Message} | {e.StackTrace}");return false;}}// 人事情報システム側で更新したB2Cユーザの認証電話番号を更新するpublic async Task UpdateAuthPhoneNumber(string objectID, string phone){try{var phoneAuthenticationMethod = new PhoneAuthenticationMethod{PhoneNumber = $"+81 {phone}",PhoneType = AuthenticationPhoneType.Mobile};await this.client.Users[objectID].Authentication.PhoneMethods["3179e48a-750b-4051-897c-87b9720928f7"].Request().PutAsync(phoneAuthenticationMethod);return true;}catch(Exception e){CommonFunction.WriteLog(logWriter, "ERROR", $"{e.Message} | {e.StackTrace}");return false;}}// 明細照会サービス側で追加したB2CユーザのユーザIDを更新するpublic async Task UpdateUserId(string objectID, string userId, string singleName){try{var user = new User{Identities = new List(){new ObjectIdentity{SignInType = "userName",Issuer = "{this.tenantName}.onmicrosoft.com",IssuerAssignedId = userId},},};await this.client.Users[objectID].Request().UpdateAsync(user);return true;}catch(Exception e){CommonFunction.WriteLog(logWriter, "ERROR", $"{e.Message} | {e.StackTrace}");return false;}}// B2Cにユーザを削除するpublic async Task DeleteUserByObjId(string objectId){try{await this.client.Users[objectId].Request().DeleteAsync();return true;}catch(Exception e){CommonFunction.WriteLog(logWriter, "ERROR", $"{e.Message} | {e.StackTrace}");return false;}}}
}

相关内容

热门资讯

话费小本创业的项目 话费小本创... 小本创业项目:快餐小吃店话费小本创业的项目随着社会压力的增大,很多人没有时间本人做饭,为了工作会选择...
2014小本创业不容错过的项目... 2014小本创业做什么比较好?其实对于大部分创业者来说,小本创业更能取得成功,今天就给大家介绍几个小...
2014小本创业不容错过 卤中... 如今,随着近年来熟食行业的快速发展,给广大的消费者带来了美味,也给投资者带来了绝好的契机,卤中仙熟食...
小本创业好项目摆地摊做什么好?... 小本创业一直以来都是比较受普通大众所喜爱的,而很多人白手起家最开始做的就是摆地摊,不要瞧不起这个小本...
重访抗战地标丨英雄之城·不能忘...   为什么要永远铭记抗战英雄?因为当“中华民族到了最危险的时候”,“用血肉筑起新的长城”并不是艺术升...
各地高校持续实施“宏志助航计划...   央视网消息:当前正值高校毕业生就业关键期,教育部要求各地高校加快实施年度“宏志助航计划”,为离校...
2018小本创业好2017十个... 1、农户住宅设计业我们知道盖房是农民生活中的头等大事,随着社会经济的发展,农户住宅也向实用、美观、个...
环保创业好项目 环保行业有哪些... 酿酒蒸馏/餐饮食堂,全国90%传统锅炉需要改造。千亿市场商机等你来分享。具有竞争性独特产品,市场唯一...
2015年有哪些小本创业好项目... 对于普通大众来说,由于资金的限制,进行小本创业是最恰当的选择.都有什么项目适宜小本创业经营呢?  第...
创业小本好项目 创业小本好项目... 生活水平越来越高,这对于刚毕业的年轻人来说或许就产生了不小的压力,进入职场的时间不长,工资也没有太高...