SSO with Azure ADFS and OWIN
Asked Answered
S

2

8

Thank you for providing help. I have a site that can authenticate with Active Directory Federated Services for Single Sign On. Currently, the way my site works is that, by default, when a user hits my site, my code attempts to log then into SSO (I use the OWIN library for this). If the user is not on our network, it fails to authenticate, and they are redirected to my companies login page, where they can provide their company credentials.

I would like to change this behavior, though. Instead, when the user hits my page, if they authenticate, it should continue as normal and they should be redirected to my site. But, if they do not authenticate, I do not want them redirected to our login page. instead, I want them to be redirected back to my site, where my code will determine what they can and cannot do on the site. I then would want to provide a link, so that they could decide to go to the login page.

I want this behavior because the majority of users of this site will not be a part of the companies network and will not be able to authenticate. SO, they should, by default, just see our home page. But, there may be times when a company member might be working from home, so wont be on our network to auto authenticate. In this case, they would then use the link that sends them to the Azure login page.

Here is the code that I am currently using (site is ASP.net, form web page (not MVC)):

Startup.Auth.vb:

Partial Public Class Startup
    Dim appSettings = ConfigurationManager.AppSettings

    Private realm As String
    Private aadInstance As String
    Private tenant As String
    Private metadata As String
    Private authority As String

    Public Sub ConfigureAuth(app As IAppBuilder)
        Try
            Dim appSettings = ConfigurationManager.AppSettings
            If (appSettings("releaseVersion") = "DEBUG") Then
                realm = ConfigurationManager.AppSettings("test_ida:RPIdentifier")
                aadInstance = ConfigurationManager.AppSettings("test_ida:AADInstance")
                tenant = ConfigurationManager.AppSettings("test_ida:Tenant")
            ElseIf (appSettings("releaseVersion") = "PROD") Then
                realm = ConfigurationManager.AppSettings("ida:RPIdentifier")
                aadInstance = ConfigurationManager.AppSettings("ida:AADInstance")
                tenant = ConfigurationManager.AppSettings("ida:Tenant")
            End If

            metadata = String.Format("{0}/FederationMetadata/2007-06/FederationMetadata.xml", aadInstance)
            authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant)

            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
            app.UseCookieAuthentication(New CookieAuthenticationOptions())
            Dim authOption As WsFederationAuthenticationOptions = New WsFederationAuthenticationOptions()

            Dim fn = Function(context)
                         context.HandleResponse()
                         context.Response.Redirect("Home/Error?message=" + context.Exception.Message)
                         Return Task.FromResult(0)
                     End Function

            Dim auth_not As WsFederationAuthenticationNotifications = New WsFederationAuthenticationNotifications() With {
                    .AuthenticationFailed = fn
                 }

            Dim auth_opt As WsFederationAuthenticationOptions = New WsFederationAuthenticationOptions() With {
                 .Wtrealm = realm,
                 .MetadataAddress = metadata,
                 .Notifications = auth_not
               }
            If (Not auth_opt.Wtrealm Is Nothing) Then
                app.UseWsFederationAuthentication(auth_opt)
            Else

            End If

        Catch ex As Exception
            Throw ex
        End Try

    End Sub


End Class

Then, on my Default.aspx.vb page load event, I do this:

  If (Not Request.IsAuthenticated) Then
        Try
            Dim newAuth As AuthenticationProperties = New AuthenticationProperties()
            newAuth.RedirectUri = "/"
            HttpContext.Current.GetOwinContext().Authentication.Challenge(newAuth, WsFederationAuthenticationDefaults.AuthenticationType)

        Catch ex As Exception
            Throw ex
        End Try

    End If

The problem is, I do not know how to attempt to authenticate the user, determine if they are authenticated, and redirect them accordingly. Any help would be greatly appreciated.

thanks

Stenosis answered 9/1, 2018 at 17:53 Comment(10)
anyone have any experience with this?Stenosis
While this sounds tricky (how to determine whether the user can authenticate or not? what if they can but don't want to?) you can rethink your design. Rather than deciding automatically, let users decide. Formally - have a starting page that doesn't require any auth (no Authorize attribute over your controller) and have all other pages that require authentication redirect to yet another login page in your site. There, have few options (links) users choose from on their own: login with Azure, login with username/password, login with another provider etc.Somerville
I have thought of this idea. First, it is a single page app, though this could still be done. Second, the customer wants the specified behavior. The majority of our users will be on our network and will auto login. But, for those who are not on our network, they most likely cannot authenticate (general public) so redirecting them to a login page would be an issue. the edge case is users who can authenticate but are off site and off our network.Stenosis
Is this just not possible? I feel like this shouldn't be that complicated. But, I don't know who to determine if someone is SSO's. If there was a way to catch a failed sign in attempt, and redirect before hitting the companies log in page, I think that would work? I just don't know how to do that using OWINStenosis
Trick here is that you have integrated auth (ntlm/kerberos based) which is auto handled by the browser. An idea would be to have a separate page that tries to auto login and does window.postMessage to indicate a succesfull auth when loaded. Then you have the main page that doesn't require any auth but you put this additional page in an iframe of 1x1 (so that it's not visible to the user). And the main page listens to message event to catch the succesfull auth from the iframe. Whenever you catch it, you can force/start the authentication as you are guaranteed it will succeed.Somerville
@WiktorZychla thanks for your response. It seems a bit hacky, but I suppose it is better than nothing. Can you provide a code example of how this would work? I would still prefer a cleaner method to do this, but assuming no other responses, it could be a solution. Thanks!Stenosis
The window.postMessage, onmessage part is just a standard way of communication between an app and an inframe hosted inside, nothing fancy so that code examples can be as simple as the official docs. However, if you feel this is a possible direction, I can rewrite this cleaner and make an answer out of this concise comment.Somerville
Yes, as I mentioned, I would like something a less "hacky" (no offense), but assuming no other responses, I'll take what I can get!Stenosis
According to my experience, that's not "hacky" but rather - a common approach involving existing means to fulfill your rather uncommon requirement. Let me know in few days if you are still interested (indeed: someone can possibly come up with something that suits you better).Somerville
Let us continue this discussion in chat.Stenosis
S
2

There is not solid/correct way how to check if anonymous user is inside your network(or I am not aware of one). Possible way is to check IP address (range) users inside your network have publicly on the Internet. This is something you can check with network administrator(s). They may tell you public IP address (range).

Once you know public IP address (range) you can check incoming request to compare if the request is coming from the known reange of IP address (range) inside RedirectToIdentityProvider function.

Dim redirectToIdentityProvider = Function(context)
    Dim isCompanyNetworkUser = companyIPAddress == context.Request.RemoteIpAddress
    ' Or relevant check for range
    ' Dim isCompanyNetworkUser = (companyIPAddressRangeStart < context.Request.RemoteIpAddress AndAlso companyIPAddressRangeEnd > context.Request.RemoteIpAddress

    If Not isCompanyNetworkUser Then
        context.State = NotificationResultState.Skipped
        context.HandleResponse()
    End If

End Function

Dim auth_not As WsFederationAuthenticationNotifications = New WsFederationAuthenticationNotifications() With {
                    .AuthenticationFailed = fn
                    .RedirectToIdentityProvider = redirectToIdentityProvider
    }

You may want to tweak it a bit as I didn't try it, but may point you to right direction.

Semipalatinsk answered 25/1, 2018 at 12:48 Comment(2)
Thanks for all your help on this, cloudikka! I hope you got the bounty for this! I did speak with our Azure admins, and the confirmed that the only way to really do this is to test for the client's source IP in the header and match it with the range of our IP. I will give this a try!Stenosis
It am not about the bounty. I was about to help. You need some help, just reach me out! I am for two week over the polar circle, but I am responsive :)Semipalatinsk
I
-1

Sorry to not providing full code example, but in my opinion :


  • You may try to bypass the sign-in page prompts, take a look here, which explain you how to :

bypass the sign-in page prompts by adding your company’s existing federated domain name to the end of the Windows Azure Management Portal URL


When users sign in using Azure AD, this feature validates users' passwords directly against your on-premises Active Directory.


I found finally that this question may be relative to your question.

Interpenetrate answered 24/1, 2018 at 11:47 Comment(1)
@A STEFANI thanks for the response, but the links you provided do not actually apply to the issue I am having. My app does seamlessly log the user into Azure, but only if they are on the same network as Azure. I need to detect if that is the case, so I can stop the auto-login if they are not on the network.Stenosis

© 2022 - 2024 — McMap. All rights reserved.