cancel
Showing results for 
Search instead for 
Did you mean: 
Graham_Alderso1
F5 Employee
F5 Employee

Introduction

With the new Office 365 sign-in experience you can capture the username entered at the O365 login page! This has been a frequent request from Office 365 users federated via SAML so users don’t have to enter their username twice when performing SP initiated logon and getting redirected from O365 for authentication.

Using the new O365 Sign-In Experience

Here’s the new sign-in experience I’m talking about:

0151T000003d74OQAQ.png

If you try the new sign-in experience, it will post the username along with the SAML AuthN request. That’s what we need to prefill the username field for them. Once you try it, a cookie is set and you’ll continue to use it unless you click the link on the sign-in page to use the old sign-in experience.

Improving Your User’s Logon Experience

(Updated iRule should work with all browsers)

when HTTP_REQUEST {
    # Check for for SAML POSTS
    if { [HTTP::uri] starts_with "/saml/idp/profile/redirectorpost/sso" && [HTTP::method] eq "POST" } {

        # Collect up to 1Mb of request content
        if { [HTTP::header exists "Content-Length"] && [HTTP::header "Content-Length"] < 1048577 } {
            set content_length [HTTP::header "Content-Length"]
        } else {
            set content_length 1048576
        }
        if { $content_length > 0 } {
            HTTP::collect $content_length
        }
    }
}

when HTTP_REQUEST_DATA {
    # Parse the username from the collected payload
    set username [URI::decode [URI::query "?[HTTP::payload]" username]]
    HTTP::release
}

when ACCESS_SESSION_STARTED {
    if { [ info exists username ] } {
        ACCESS::session data set session.logon.last.username $username
    }
}

Now you’ll need to customize your access policy. You will need to replace your existing Logon Page object with a new macro. The is the end result we’re looking for:

0151T000003d74PQAQ.png

First is the Username Check. You’ll need to add an empty object (click +, general purpose tab, empty). You want to create a branch rule, use advanced, and enter the following:

expr { [mcget {session.logon.last.username}] ne "" }

0151T000003d74QQAQ.png

Next, the AD Query (authentication tab). The purpose of this object is to find the matching AD account for the email address that Office 365 sends. Normally it will match your user’s UserPrincipalName (UPN), so you can see we’ve customized the search filter to look for a matching UPN instead of the default which is a matching sAMAccountName.

If that’s not the case, you can change the search filter here, for instance yours might match the mail attribute if you’ve not modified your UPNs as is standard practice for O365. Also note that I’ve changed the branch rule, we don’t want the default of checking the primary group ID. We just want to know if the query was successful (did we find a match).

Here's what you'll enter into the search filter assuming you have set UPN to match O365 email.

userPrincipalName=%{session.logon.last.username}

If all your sAMAccountNames are just the first part of the email address, you can leave out the AD Query, Variable Assign, and Logon Page – Username Found But Invalid objects and just check the box for “Split Domain Name From Full Username” on the Logon Page – Username Found object.

0151T000003d74RQAQ.png 0151T000003d74SQAQ.png

Next, the variable assign (assignment tab). This object takes the sAMAccountName we got from the AD Query and puts that value into the username variable. Put session.logon.last.username on the left side, and on the right side select AAA attribute, AD, Use user’s attribute, and the attribute name is sAMAccountName.

0151T000003d74TQAQ.png

Finally, the logon page (logon tab) for when O365 sends us the email and we find a matching username. Note that it’s just like your normal logon page, but you set the “Read Only” value to yes for the username field. This will cause the matching username to show up there.

0151T000003d74UQAQ.png

Notice there are two other logon pages there, one for when no username is found and one for when there is no AD match to get a sAMAccountName for. Those are standard, nothing special. I’ll include pictures here anyway.

0151T000003d74VQAQ.png

0151T000003d74WQAQ.png

And now when you logon using the new O365 sign in experience, this is what you’ll see:

0151T000003d74XQAQ.png

Give it a try, you'll make your users happy!

Comments
AP
Nimbostratus
Nimbostratus

Hi,

 

I also found that signing in via a different Microsoft landing page results in a different POST request to the Idp. Different again from the new and old sign-in experience.

 

The particular landing page I came across was office365.com, which redirects you to products.office.com. If you click sign in from here, the sign-in page looks very similar to the O365 new sign-in experience, however it's actually login.live.com.

 

The POST request to the IDP still has a referer of login.microsoftonline.com, however the username parameter doesn't exist. Fortunately however, the username is available in the Referer header with "login_hint=". The iRule below is based on Grahams with the added functionality of being able to grab the login_hint username when available.

 

The iRule is working in a Dev environment for these use-cases, however there may be others. By default these will still require manual input of username on the Idp. It could also be possible to make the iRule more efficient by putting the new sign in experience conditional statements (payload capture) first, assuming it is the most common sign-in method for a given client base.

 

The following iRule attempts to capture the Username for Office365 SAML SP initiated authentication requests to allow pre-population of username on logon page. This iRule requires specific logic to be built in the VPE. when HTTP_REQUEST { Enable/disable iRule debug logging. set o365usernamedebug 0 Check for for SAML POSTS if { [HTTP::uri] starts_with "/saml/idp/profile/redirectorpost/sso" && [HTTP::method] eq "POST" } { if {$o365usernamedebug} {log local0.info "Request is a SAML POST"} For logins via certain Microsoft landing pages - Check if Referer header contains login_hint if { [HTTP::header exists "Referer"] } { if {$o365usernamedebug} {log local0.info "POST Request has a Referer header"} set refererHeader [HTTP::header "Referer"] if { $refererHeader contains "login_hint="} { set username [URI::decode [URI::query $refererHeader login_hint]] if {$o365usernamedebug} {log local0.info "Referer header contains login_hint. Username = $username "} } unset refererHeader } For logins via new sign-in experience - Collect Payload to enable a search for a username parameter Collect up to 1Mb of request content if { ![ info exists username ] } { if {$o365usernamedebug} {log local0.info "login_hint not available. Looking for username in payload"} if { [HTTP::header exists "Content-Length"] && [HTTP::header "Content-Length"] < 1048577 } { set contentLength [HTTP::header "Content-Length"] } else { set contentLength 1048576 } if { $content_length > 0 } { HTTP::collect $contentLength } } } } when HTTP_REQUEST_DATA { For logins via new sign-in experience - check payload for username parameter if { [ info exists contentLength ] } { Parse the username from the collected payload if { [HTTP::payload] contains "username="} { set username [URI::decode [URI::query "?[HTTP::payload]" username]] if {$o365usernamedebug} {log local0.info "Username parameter exists. Username = $username "} HTTP::release } } } when ACCESS_SESSION_STARTED { if { [ info exists username ] } { ACCESS::session data set session.sso.custom.o365mail $username } }
Songseajoon_222
Nimbostratus
Nimbostratus

Is the AD Server I set here the AD of the o365?

 

AP
Nimbostratus
Nimbostratus

Hi Songseajoon,

 

It will be the AD Server that you want your SAML IdP to use. If you have self-hosted AD and your F5's are in the same location (e.g. on-premise), then you'd generally be looking at using those AD servers.

 

You probably want to talk to your AD team if these concepts are unclear.

 

Gav_387282
Nimbostratus
Nimbostratus

Great post!

 

Slight modification if being redirected from mail.office365.com. This will capture the username if found and write it to the session.logon.last.username only if the URI and referrer are matched. You can then use this in VPE as described above to pre-populate username

 

when HTTP_REQUEST { if { [HTTP::uri] starts_with "/adfs/ls/" } { if { [HTTP::header exists "Referer"] && [HTTP::header "Referer"] contains "office365.com" } { set received_requesturl [HTTP::uri] log local0. "Starting GetURIUsername" log local0. $received_requesturl if { $received_requesturl contains "username="} { log local0. "requested URL has username" set username [URI::decode [URI::query $received_requesturl username]] log local0. "Username detected as $username" } unset received_requesturl } } } when ACCESS_SESSION_STARTED { if { [ info exists username ] } { ACCESS::session data set session.logon.last.username $username log local0. "Username set to $username" } }
Jad_Tabbara__J1
Cirrostratus
Cirrostratus

Hello Graham,

 

Thanks for you article.

 

In your case F5 is IDP, check now the use case where F5 is SP, it is easier because Azure IDP support a "login_hint" as a query parameter. By adding it on F5 SAML Request it allows you to bypass the Azure Login Page.

 

https://devcentral.f5.com/s/articles/Bypass-Azure-Login-Page-by-adding-a-login-hint-in-the-SAML-Request?page=1

 

Regards

 

 

 

 

Version history
Last update:
‎10-Aug-2017 06:00
Updated by:
Contributors