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

jason
  • 3,821
  • 10
  • 63
  • 120
  • anyone have any experience with this? – jason Jan 13 '18 at 20:43
  • 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. – Wiktor Zychla Jan 17 '18 at 14:49
  • 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. – jason Jan 17 '18 at 14:59
  • 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 OWIN – jason Jan 17 '18 at 15:01
  • 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. – Wiktor Zychla Jan 17 '18 at 15:12
  • @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! – jason Jan 17 '18 at 15:32
  • 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. – Wiktor Zychla Jan 17 '18 at 16:06
  • 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! – jason Jan 17 '18 at 16:10
  • 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). – Wiktor Zychla Jan 17 '18 at 16:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/163340/discussion-between-jason-and-wiktor-zychla). – jason Jan 17 '18 at 16:44

2 Answers2

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.

dropoutcoder
  • 2,627
  • 2
  • 14
  • 32
  • 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! – jason Jan 26 '18 at 16:47
  • 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 :) – dropoutcoder Jan 27 '18 at 00:13
-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.

A. STEFANI
  • 6,707
  • 1
  • 23
  • 48
  • @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. – jason Jan 24 '18 at 21:41