Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Authenticating Users Through OAuth2 in Azure

0.00/5 (No votes)
17 Apr 2015 1  
How to do authentication through Azure with OAuth2. Clearing up some not so well documented thing in Azure

Introduction

It took me 3 days of debugging and a lot of searching / trying / experimenting to get this thing done. The documentation in Azure is not very good and I could not find any good articles on the web. But after putting together bits and pieces of information, I finally got it to work.

Background

In my case, we are developing enterprise applications and want to implement SingleSignOn through Azure for our customer.

All users have to be registered up front by registering them in our system. This way, we know who they are and which company they belong to. This is a must for us as we are running a multitenant application where all customers are running in the same web application.

I decided not to use OWIN.OAuth package to do the authentication as I would like to know what is happening in my application.

Using the Code

The whole point of my code is to get the JWT from Azure to find out if the user is logged in and the user name from his/her domain.

Let's start:

Here is the documentation from Microsoft that I could find on the subject.

So I followed this and met a speedbump on...

Also, take a look at my question on Stackoverflow.

JavaScript
        {"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}  

So here is my code with comments to get this to work. It's implemented in a good old .aspx page. :)

VB.NET
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

Points of Interest

The

VB.NET
?api-version=1.0

parameter that is included in the URL when you copy the address from Azure must be removed. Good job, Microsoft.

You must use the:

VB.NET
https://graph.windows.net

As the resource parameter, this might not be that strange, but the error message in Azure is and I found no good documentation on it.

History

  • 17th April, 2015: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here