azure-active-directorydotnetopenauth

Trying to log into Azure AD using OpenID and dotnetopenauth but getting "No OpenID endpoint found."


I've searched SO for similar questions and reviewed all of them but I haven't found one that helped me with the issue that I'm having.

I'm trying to add authentication to our legacy webforms application for Azure AD. Another developer already added OpenID support so I thought the easiest way to get this to work would be to use the OpenID endpoint in AzureAD and the exisiting code in our application. However, I have not been able to get it to work.

I don't know if the problem is in the endpoint URL that I'm using, the code we are using to try and connect, in how I have the AzureAD application configured, or a fundamental misunderstanding about what I'm trying to do.

This is supposed to be a multi-tenant application meaning, if I understood this properly, users can sign in with accounts in our Azure AD or other Azure ADs and then our application can choose to grant not grant access. But, I haven't even been able to get it to acknowledge that I'm targeting a valid OpenID endpoint much less even log in with a user from our own Azure AD.

The OpenID code is written using http://dotnetopenauth.net/ and the error occurs when we call CreateRequest()

Using OpenID As New OpenIdRelyingParty
Dim LoginEndpoint = "<azure ad endpoint goes here>"
Dim Request As IAuthenticationRequest = OpenID.CreateRequest(LoginEndpoint)
End Using

For LoginEndpoint I have tried every combination that I can think of.

As a baseline, I tested with Yahoo first, which works. I used the endpoint https://me.yahoo.com

Then, following the MS Docs, I found https://login.microsoftonline.com/MyEXAMPLE.com/v2.0/.well-known/openid-configuration or https://login.microsoftonline.com/MyEXAMPL-GUID-0000-0000-000000000000/v2.0/.well-known/openid-configuration

and in the JSON returned I took the authorization_endpoint which looked like https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize

The authorization_endpoint is what I have tried passing in as LoginEndpoint in the code example above.

When that didn't work, I tried looking at the Yahoo page and the Azure AD authorization_endpoint in a browser and the Azure AD one was reporting some missing fields and the Yahoo one reports 404 so I'm guessing these weren't meant to be visited directly in a browser.

But, just to exhaust all possibilities I tried to give the Azure AD one the values that it wanted by passing them on the query string. So I ended up with something like https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize?client_id=CLIENTID-GUID-0000-0000-000000000000&scope=openid

I got the client_id by going into the Azure AD and finding the Application that I registered and I got the value labeled Application ID. I think I did this because I have another application that works (asp.net core instead of asp.net framework webforms) and we used Application ID as Client ID and it worked, but let me know if I should have gotten client ID from somewhere else.

Anyway, when I did this and plugged it into the browser it showed the MS login page with no error, but when I tried using that URL in my code as LoginEndpoint it still didn't work.

I tried looking at the network traffic in Fiddler that the web server sends and receives when logging into Yahoo vs Azure AD and while I found some interesting stuff, I didn't find anything that helped me solve the problem. Basically, I think I understand why dotnetopenauth.net doesn't like the url I gave it, but that didn't help me fix it. This isn't my area of expertise but I didn't see the stuff that I think dotnetopenauth.net is looking for when it does "discovery" in the HTML and HTTP Headers returned, so it thinks its not an OpenID endpoint.

So next I turned on logging and tried again with both versions of my endpoint url but it didn't show me anything helpful but maybe it will be helpful to someone reading this.

2019-01-02 23:26:30,450 (GMT-5) [11] INFO  DotNetOpenAuth - DotNetOpenAuth.Core, Version=4.3.4.13329, Culture=neutral, PublicKeyToken=2780ccd10d57b246 (official)
2019-01-02 23:26:30,450 (GMT-5) [11] INFO  DotNetOpenAuth - DotNetOpenAuth.Core, Version=4.3.4.13329, Culture=neutral, PublicKeyToken=2780ccd10d57b246 (official)
2019-01-02 23:26:30,517 (GMT-5) [11] INFO  DotNetOpenAuth - Reporting will use isolated storage with scope: User, Domain, Assembly
2019-01-02 23:26:30,517 (GMT-5) [11] INFO  DotNetOpenAuth - Reporting will use isolated storage with scope: User, Domain, Assembly
2019-01-02 23:26:34,738 (GMT-5) [11] INFO  DotNetOpenAuth.Messaging.Channel - Scanning incoming request for messages: http://localhost:49221/Initial/Login.aspx?ReturnUrl=%2fDashboard.aspx
2019-01-02 23:26:34,738 (GMT-5) [11] INFO  DotNetOpenAuth.Messaging.Channel - Scanning incoming request for messages: http://localhost:49221/Initial/Login.aspx?ReturnUrl=%2fDashboard.aspx
2019-01-02 23:26:34,741 (GMT-5) [11] DEBUG DotNetOpenAuth.Messaging.Channel - Incoming HTTP request: GET http://localhost:49221/Initial/Login.aspx?ReturnUrl=%2fDashboard.aspx
2019-01-02 23:26:34,741 (GMT-5) [11] DEBUG DotNetOpenAuth.Messaging.Channel - Incoming HTTP request: GET http://localhost:49221/Initial/Login.aspx?ReturnUrl=%2fDashboard.aspx
2019-01-02 23:26:57,115 (GMT-5) [8] INFO  DotNetOpenAuth.Messaging.Channel - Scanning incoming request for messages: http://localhost:49221/Initial/Login.aspx?ReturnUrl=%2f
2019-01-02 23:26:57,115 (GMT-5) [8] INFO  DotNetOpenAuth.Messaging.Channel - Scanning incoming request for messages: http://localhost:49221/Initial/Login.aspx?ReturnUrl=%2f
2019-01-02 23:26:57,116 (GMT-5) [8] DEBUG DotNetOpenAuth.Messaging.Channel - Incoming HTTP request: GET http://localhost:49221/Initial/Login.aspx?ReturnUrl=%2f
2019-01-02 23:26:57,116 (GMT-5) [8] DEBUG DotNetOpenAuth.Messaging.Channel - Incoming HTTP request: GET http://localhost:49221/Initial/Login.aspx?ReturnUrl=%2f
2019-01-02 23:33:39,365 (GMT-5) [10] DEBUG DotNetOpenAuth.OpenId - .NET Uri class path compression overridden.
2019-01-02 23:33:39,365 (GMT-5) [10] DEBUG DotNetOpenAuth.OpenId - .NET Uri class path compression overridden.
2019-01-02 23:33:39,433 (GMT-5) [10] DEBUG DotNetOpenAuth.Http - HTTP GET https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize?client_id=CLIENTID-GUID-0000-0000-000000000000&scope=openid
2019-01-02 23:33:39,433 (GMT-5) [10] DEBUG DotNetOpenAuth.Http - HTTP GET https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize?client_id=CLIENTID-GUID-0000-0000-000000000000&scope=openid
2019-01-02 23:33:39,855 (GMT-5) [10] DEBUG DotNetOpenAuth.Yadis - HTML discovery failed to find any endpoints.
2019-01-02 23:33:39,855 (GMT-5) [10] DEBUG DotNetOpenAuth.Yadis - HTML discovery failed to find any endpoints.
2019-01-02 23:33:39,865 (GMT-5) [10] INFO  DotNetOpenAuth.Yadis - Performing discovery on user-supplied identifier: https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize?client_id=CLIENTID-GUID-0000-0000-000000000000&scope=openid
2019-01-02 23:33:39,865 (GMT-5) [10] INFO  DotNetOpenAuth.Yadis - Performing discovery on user-supplied identifier: https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize?client_id=CLIENTID-GUID-0000-0000-000000000000&scope=openid
2019-01-02 23:33:39,869 (GMT-5) [10] DEBUG DotNetOpenAuth.Yadis - Filtering and sorting of endpoints did not affect the list.
2019-01-02 23:33:39,869 (GMT-5) [10] DEBUG DotNetOpenAuth.Yadis - Filtering and sorting of endpoints did not affect the list.
2019-01-02 23:51:30,214 (GMT-5) [17] DEBUG DotNetOpenAuth.Http - HTTP GET https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize
2019-01-02 23:51:30,214 (GMT-5) [17] DEBUG DotNetOpenAuth.Http - HTTP GET https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize
2019-01-02 23:51:30,492 (GMT-5) [17] DEBUG DotNetOpenAuth.Yadis - HTML discovery failed to find any endpoints.
2019-01-02 23:51:30,492 (GMT-5) [17] DEBUG DotNetOpenAuth.Yadis - HTML discovery failed to find any endpoints.
2019-01-02 23:51:30,493 (GMT-5) [17] INFO  DotNetOpenAuth.Yadis - Performing discovery on user-supplied identifier: https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize
2019-01-02 23:51:30,493 (GMT-5) [17] INFO  DotNetOpenAuth.Yadis - Performing discovery on user-supplied identifier: https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize
2019-01-02 23:51:30,495 (GMT-5) [17] DEBUG DotNetOpenAuth.Yadis - Filtering and sorting of endpoints did not affect the list.
2019-01-02 23:51:30,495 (GMT-5) [17] DEBUG DotNetOpenAuth.Yadis - Filtering and sorting of endpoints did not affect the list.

Please let me know any additional information that you need and I will add it.

EDIT 2019-01-03 5:04PM Eastern if I add ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3

I get the same exception in my application but the log now shows the following errors

2019-01-03 17:00:31,925 (GMT-5) [7] DEBUG DotNetOpenAuth.Http - HTTP GET https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize
2019-01-03 17:00:32,374 (GMT-5) [7] ERROR DotNetOpenAuth.Http - SendFailure connecting to https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize
2019-01-03 17:00:32,374 (GMT-5) [7] ERROR DotNetOpenAuth.Http - SendFailure connecting to https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize
2019-01-03 17:00:32,376 (GMT-5) [7] ERROR DotNetOpenAuth.Yadis - Error while performing discovery on: "https://login.microsoftonline.com/ANOTHER0-GUID-0000-0000-000000000000/oauth2/v2.0/authorize": DotNetOpenAuth.Messaging.ProtocolException: Error occurred while sending a direct message or getting the response. ---> System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a send. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
   at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   --- End of inner exception stack trace ---
   at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.FixedSizeReader.ReadPacket(Byte[] buffer, Int32 offset, Int32 count)
   at System.Net.Security.SslState.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ForceAuthentication(Boolean receiveFirst, Byte[] buffer, AsyncProtocolRequest asyncRequest)
   at System.Net.Security.SslState.ProcessAuthentication(LazyAsyncResult lazyResult)
   at System.Net.TlsStream.CallProcessAuthentication(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Net.TlsStream.ProcessAuthentication(LazyAsyncResult result)
   at System.Net.TlsStream.Write(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.PooledStream.Write(Byte[] buffer, Int32 offset, Int32 size)
   at System.Net.ConnectStream.WriteHeaders(Boolean async)
   --- End of inner exception stack trace ---
   at System.Net.HttpWebRequest.GetResponse()
   at DotNetOpenAuth.Messaging.StandardWebRequestHandler.GetResponse(HttpWebRequest request, DirectWebRequestOptions options)
   --- End of inner exception stack trace ---
   at DotNetOpenAuth.Messaging.StandardWebRequestHandler.GetResponse(HttpWebRequest request, DirectWebRequestOptions options)
   at DotNetOpenAuth.Messaging.UntrustedWebRequestHandler.GetResponse(HttpWebRequest request, DirectWebRequestOptions options)
   at DotNetOpenAuth.Yadis.Yadis.Request(IDirectWebRequestHandler requestHandler, Uri uri, Boolean requireSsl, String[] acceptTypes)
   at DotNetOpenAuth.Yadis.Yadis.Discover(IDirectWebRequestHandler requestHandler, UriIdentifier uri, Boolean requireSsl)
   at DotNetOpenAuth.OpenId.UriDiscoveryService.Discover(Identifier identifier, IDirectWebRequestHandler requestHandler, Boolean& abortDiscoveryChain)
   at DotNetOpenAuth.OpenId.IdentifierDiscoveryServices.Discover(Identifier identifier)
   at DotNetOpenAuth.OpenId.RelyingParty.AuthenticationRequest.Create(Identifier userSuppliedIdentifier, OpenIdRelyingParty relyingParty, Realm realm, Uri returnToUrl, Boolean createNewAssociationsAsNeeded)

with this change the code would look like:

Using OpenID As New OpenIdRelyingParty
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
Dim LoginEndpoint = "<azure ad endpoint goes here>"
Dim Request As IAuthenticationRequest = OpenID.CreateRequest(LoginEndpoint)
End Using

Solution

  • I'm posting this as an answer to my own question, but i want to caveat that I'm not 100% sure I'm correct on this, but I'm pretty sure.

    I think my problem was that DotNetOpenAuth.net or at least the piece of it that I was using was trying to make an OpenID 2 connection where I was trying to connect to an OpenID Connect endpoint.

    When i started this, I didn't realize that those were different and ate up a lot of time until I came across an explanation.

    There does appear to be an example in the DotNetOpenAuth.net Git repo for connecting to an AzureAD endpoint, but it appeared to use completly different code and objects that what I had in my code and what I could find in the DotNetOpenAuth.net documentation, at the time of this writing. Maybe I just couldn't find it, but I didn't see it anywhere.

    So I just decided, that since I had to use something different anyway I would just use Microsoft's OpenID Connect and AzureAD code.