65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Azure 中通过 OAuth2 验证用户

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.50/5 (3投票s)

2015年4月17日

CPOL

1分钟阅读

viewsIcon

23598

如何在 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 日:初始版本
© . All rights reserved.