在 Azure 中通过 OAuth2 验证用户






3.50/5 (3投票s)
如何在 Azure 中使用 OAuth2 进行身份验证。澄清一些 Azure 中文档记录不完善的地方。
引言
我花了三天时间调试,并进行了大量的搜索/尝试/实验才完成这项工作。Azure 的文档不太好,而且我找不到任何相关的优秀文章。但经过整合各种信息,我最终成功了。
背景
在我的案例中,我们正在开发企业应用程序,并希望为我们的客户通过 Azure 实现 SingleSignOn
(单点登录)。
所有用户必须提前注册,通过在我们的系统中注册他们来实现。这样,我们知道他们是谁以及他们属于哪个公司。这对我们来说是必须的,因为我们正在运行一个多租户应用程序,所有客户都在同一个 Web 应用程序中运行。
我决定不使用 OWIN.OAuth
包进行身份验证,因为我想知道我的应用程序中发生了什么。
Using the Code
我的代码的重点是从 Azure 获取 JWT,以确定用户是否已登录以及用户的域名。
开始吧
这是微软提供的关于该主题的文档,是我能找到的。
所以我按照这个文档操作,遇到一个障碍…
另外,请查看我在 Stackoverflow 上的问题。
{"error":"invalid_grant","error_description":
"AADSTS65001: No permission to access user information is configured for
'7686eb91-cfcd-4b89-b4db-422b52205848' application, or it is expired or revoked.
\r\nTrace ID: a631793a-a02b-4d08-abbb-bc7340a87409\r\nCorrelation ID:
335493ad-685a-4147-9e03-218a0b2acab3\r\nTimestamp: 2015-04-15 06:38:27Z",
"error_codes":[65001],"timestamp":"2015-04-15 06:38:27Z",
"trace_id":"a631793a-a02b-4d08-abbb-bc7340a87409","correlation_id"
:"335493ad-685a-4147-9e03-218a0b2acab3","submit_url":null,"context":null}
所以这是我的代码,带有注释,以使其正常工作。它是在一个经典的 .aspx 页面中实现的。:)
Public Class Azure
Inherits System.Web.UI.Page
Const AzureEndPoint As String = "https://login.windows.net/common/oauth2/authorize/"
Const AzureClientId As String = "7686eb91-cfcd-4b89-b4db-422b52205848"
Const AzureSecret = "1S3YAHs4UE4uIaeQavKShCn798/llcwQje4bjjpQI88="
Private Function MyUrl() As String
Return HttpContext.Current.Request.Url.Scheme & "://" & _
HttpContext.Current.Request.Url.Authority & HttpContext.Current.Request.Url.AbsolutePath
End Function
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If HttpContext.Current.Request("code") Is Nothing _
Then 'No code in the url, send the user to Azure for Authentication.
SendToAzure()
Return
End If
HandleAzureResponse(HttpContext.Current.Request("code"), _
HttpContext.Current.Request("state")) 'We have a code. _
Use it to get the information about the user from Azure
End Sub
Public Sub SendToAzure()
Dim params As New Dictionary(Of String, String)
params.Add("response_type", "code")
params.Add("redirect_uri", MyUrl)
params.Add("client_id", AzureClientId)
params.Add("state", HttpContext.Current.Request.Url.Query)
Response.Redirect(AzureEndPoint & "?" & createFromDic(params))
End Sub
Public Sub HandleAzureResponse(ByVal code As String, retUrl As String)
Dim result As HttpResponseMessage
'Composing the form to post to azure
Dim params As New Dictionary(Of String, String)
params.Add("client_id", AzureClientId)
params.Add("client_secret", AzureSecret)
params.Add("code", code)
params.Add("grant_type", "authorization_code") _
'client_credentials authorization_code clinent_credentials made it work _
at first but that did not give me the JWT back
params.Add("redirect_uri", MyUrl)
'This is the URI of my application. But this is not what we are looking for at this moment.
'params.Add("resource", _
"https://petterinfotjenester.onmicrosoft.com/ITAS_SSO") _
'<- This gives another strange error about the same application as the ClientId..
'We want the graph data from the directory. Obviously this is an application from Microsoft. _
This I found by luck somewhere on the internet.
params.Add("resource", "https://graph.windows.net")'<- Important. _
This tella azure what kind of data you want.
Dim fromDic = createFromDic(params)
Dim content As New System.Net.Http.StringContent(fromDic, System.Text.Encoding.UTF8, _
"application/x-www-form-urlencoded")
Using wc As New HttpClient
'Dim url = String.Format("264d5697-62aa-4771-829c-39828b5309ae/oauth2/token?api-version=1.0") _
'<- Does not work the api-version=1.0 param makes must be removed. _
The guid is not needed I believe so i replaced it with common.
Dim url = "common/oauth2/token"
wc.BaseAddress = New Uri("https://login.windows.net/")
result = wc.PostAsync(url, content).Result
End Using
Dim value = result.Content.ReadAsStringAsync.Result
Dim deserializeObject = Newtonsoft.Json.JsonConvert.DeserializeObject(Of Oauth2Response)(value)
'And now we can do what ever we want with the deserializeObject.
End Sub
'Loop through all items in a dictionary and crate URLEncoded values.
Private Function createFromDic(ByVal dictionary As Dictionary(Of String, String)) As String
Dim ret As New List(Of String)
For Each kv In dictionary
ret.Add(kv.Key & "=" & UrlEncode(kv.Value))
'ret.Add(kv.Key & "=" & kv.Value)
Next
Return Join(ret.ToArray, "&")
End Function
End Class
Public Class Oauth2Response
Public access_token As String
Public token_type As String
Public expires_in As Integer
Private _id_token As String
Public Property id_token As String
Get
Return _id_token
End Get
Set(value As String)
'The id token received from Azure is actually 3 separate values separated with a "."
idValues = value.Split("."c)
_id_token = value
End Set
End Property
Private idValues As String()
Public ReadOnly Property EncryptionMethod As String
Get
Return idValues(0)
End Get
End Property
Public ReadOnly Property AzureJwt As Azure_JWT
Get
Dim values = idValues(1)
'Must do this to be sure that the length of the Base64 string is a multiple of 4,
'this also caused me some headache...
For x = 1 To (values.Length Mod 4)
values += "="
Next
Dim fromBase64String = Convert.FromBase64String(values)
Dim getString = Encoding.UTF8.GetString(fromBase64String)
Return Newtonsoft.Json.JsonConvert.DeserializeObject(Of Azure_JWT)(getString)
'Using JSON.Net to deserialize the object
End Get
End Property
Public ReadOnly Property Sig As String
Get
Return idValues(2)
End Get
End Property
End Class
Public Class Azure_JWT
Public aud As String
Public iss As String
Public iat As String
Public nbf As String
Public exp As String
Public ver As String
Public tid As String
Public oid As String
Public upn As String
Public unique_name As String
Public [sub] As String
Public family_name As String
Public given_name As String
End Class
关注点
该
?api-version=1.0
复制 Azure 地址时包含在 URL 中的参数必须删除。干得好,微软。
您必须使用
https://graph.windows.net
作为资源参数,这可能并不奇怪,但 Azure 中的错误消息却让我困惑,而且我没有找到任何相关的良好文档。
历史
- 2015 年 4 月 17 日:初始版本