APM Sharepoint authentication
Problem this snippet solves:
Updated version to support Webdav with windows explorer after Nicolas's comment.
APM is a great authentication service but it does it only with forms.
The default behavior is to redirect user to /my.policy to process VPE. this redirect is only supported for GET method.
Sharepoint provide 3 different access types:
- browsing web site with a browser
- Editing documents with Office
- browser folder with webdav client (or editing documents with libreoffice through webdav protocol)
This irule display best authentication method for each of these access types:
- browsers authenticate with default authentication method (form based authentication)
- Microsoft office authenticate with Form based authentication (with support of MS-OFBA protocol)
- Libreoffice and webdav clients authenticate with 401 basic authentication
Form based authentication (browser and Microsoft office) is compatible (validated for one customer) with SAML authentication
Editing documents is managed with a persistent cookie expiring after 5 minutes. to be shared between IE and Office, it requires :
- cookie is persistent (expiration date instead of deleted at the end of session)
- web site defined as "trusted sites" in IE.
How to use this snippet:
install this irule and enable it on the VS.
Code :
when RULE_INIT { array set static::MSOFBA { ReqHeader "X-FORMS_BASED_AUTH_REQUIRED" ReqVal "/sp-ofba-form" ReturnHeader "X-FORMS_BASED_AUTH_RETURN_URL" ReturnVal "/sp-ofba-completed" SizeHeader "X-FORMS_BASED_AUTH_DIALOG_SIZE" SizeVal "800x600" } set static::ckname "MRHSession_SP" set static::Basic_Realm_Text "SharePoint Authentication" } when HTTP_REQUEST { set apmsessionid [HTTP::cookie value MRHSession] set persist_cookie [HTTP::cookie value $static::ckname] set clientless_mode 0 set form_mode 0 # Identify User-Agents type if {[HTTP::header exists "X-FORMS_BASED_AUTH_ACCEPTED"] && (([HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "t") || ([HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "f"))} { set clientless_mode 0; set form_mode 1 } else { switch -glob [string tolower [HTTP::header "User-Agent"]] { "*microsoft-webdav-miniredir*" { set clientless_mode 1 } "*microsoft data access internet publishing provider*" - "*office protocol discovery*" - "*microsoft office*" - "*non-browser*" - "msoffice 12*" { set form_mode 1 } "*mozilla/4.0 (compatible; ms frontpage*" { if { [ string range [getfield [string tolower [HTTP::header "User-Agent"]] "MS FrontPage " 2] 0 1] > 12 } { set form_mode 1 } else { set clientless_mode 1 } } "*mozilla*" - "*opera*" { set clientless_mode 0 } default { set clientless_mode 1 } } } if { $clientless_mode || $form_mode } { if { [HTTP::cookie exists "MRHSession"] } {set apmstatus [ACCESS::session exists -state_allow $apmsessionid]} else {set apmstatus 0} if { !($apmstatus) && [HTTP::cookie exists $static::ckname] } {set apmpersiststatus [ACCESS::session exists -state_allow $persist_cookie]} else {set apmpersiststatus 0} if { ($apmpersiststatus) && !($apmstatus) } { # Add MRHSession cookie for non browser user-agent first request and persistent cookie present if { [catch {HTTP::cookie insert name "MRHSession" value $persist_cookie} ] } {log local0. "[IP::client_addr]:[TCP::client_port] : TCL error on HTTP cookie insert MRHSession : URL : [HTTP::host][HTTP::path] - Headers : [HTTP::request]"} else {return} } } else { return } if { $clientless_mode && !($apmstatus)} { if { !([HTTP::header Authorization] == "") } { set clientless(insert_mode) 1 set clientless(username) [ string tolower [HTTP::username] ] set clientless(password) [HTTP::password] binary scan [md5 "$clientless(password)"] H* clientless(hash) set user_key "$clientless(username).$clientless(hash)" set clientless(cookie_list) [ ACCESS::user getsid $user_key ] if { [ llength $clientless(cookie_list) ] != 0 } { set clientless(cookie) [ ACCESS::user getkey [ lindex $clientless(cookie_list) 0 ] ] if { $clientless(cookie) != "" } { HTTP::cookie insert name MRHSession value $clientless(cookie) set clientless(insert_mode) 0 } } if { $clientless(insert_mode) } { HTTP::header insert "clientless-mode" 1 HTTP::header insert "username" $clientless(username) HTTP::header insert "password" $clientless(password) } unset clientless } else { HTTP::respond 401 WWW-Authenticate "Basic realm=\"$static::Basic_Realm_Text\"" Set-Cookie "MRHSession=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT; path=/" Connection close return } } elseif {$form_mode && !($apmstatus) && !([HTTP::path] equals $static::MSOFBA(ReqVal))}{ HTTP::respond 403 -version "1.1" noserver \ $static::MSOFBA(ReqHeader) "https://[HTTP::host]$static::MSOFBA(ReqVal)" \ $static::MSOFBA(ReturnHeader) "https://[HTTP::host]$static::MSOFBA(ReturnVal)" \ $static::MSOFBA(SizeHeader) $static::MSOFBA(SizeVal) \ "Connection" "Close" return } } when HTTP_RESPONSE { # Insert persistent cookie for html content type and private session if { [HTTP::header "Content-Type" ] contains "text/html" } { HTTP::cookie remove $static::ckname HTTP::cookie insert name $static::ckname value $apmsessionid path "/" HTTP::cookie expires $static::ckname 120 relative HTTP::cookie secure $static::ckname enable } # Insert session cookie if session was recovered from persistent cookie if { ([info exists "apmpersiststatus"]) && ($apmpersiststatus) } { HTTP::cookie insert name MRHSession value $persist_cookie path "/" HTTP::cookie secure MRHSession enable } } when ACCESS_SESSION_STARTED { if {([info exists "clientless_mode"])} { ACCESS::session data set session.clientless $clientless_mode } if { [ info exists user_key ] } { ACCESS::session data set "session.user.uuid" $user_key } } when ACCESS_POLICY_COMPLETED { if { ([info exists "clientless_mode"]) && ($clientless_mode) && ([ACCESS::policy result] equals "deny") } { ACCESS::respond 401 noserver WWW-Authenticate "Basic realm=$static::Basic_Realm_Text" Connection close ACCESS::session remove } } when ACCESS_ACL_ALLOWED { switch -glob [string tolower [HTTP::path]] { "/sp-ofba-form" { ACCESS::respond 302 noserver Location "https://[HTTP::host]$static::MSOFBA(ReturnVal)" } "/sp-ofba-completed" { ACCESS::respond 200 content {Authenticated Good Work, you are Authenticated } noserver } "*/signout.aspx" { # Disconnect session and redirect to APM logout Page ACCESS::respond 302 noserver Location "/vdesk/hangup.php3" return } "/_layouts/accessdenied.aspx" { # Disconnect session and redirect to APM Logon Page if {[string tolower [URI::query [HTTP::uri] loginasanotheruser]] equals "true" } { ACCESS::session remove ACCESS::respond 302 noserver Location "/" return } } default { # No Actions } } }
Tested this on version:
11.5- garrettNimbostratus
Hello,
Has anyone got this working on macOS? This iRule works great on Windows based machines to open up files in Word/Excel/Powerpoint app but on macos, it authenticates, but never opens the actual document.
- Shawn_ConwayCirrus
Thanks!! We have you seen weird behavior in the form page and outlook client. We use SharePoint calendar services where you can add it to your outlook calendar. It works great prompting our form based login, but the form page does not always load our custom form page images. Have you seen this before? Thanks!!
- JoeTheFifthAltostratus
Thanks for the JS error fix Kai. I was going to start digging into this as this was one of the last things annoying me with my policy profile nd then saw your post.
Cheers !
- abdul_rahman_63Nimbostratus
I applied the rule but its asking multiple time login. Once am authenticated via APM and I access the file on sharepoint, when we try to open it, we re redirected to APM login page again.
- Stanislas_Piro2Cumulonimbus
the goal of the persistent cookie is to support cookie share between IE and office.
 
this feature is also done by this irule with a 2 minutes expiration time cookie to make it more secure.
 
I don't ever work on this code but on the v2 available here
 
- Pål_Andre_RopstNimbostratus
Hello Stanislas,
Thanks for your reply. I did not know that only microsoft products allowed the sharing of Cookies.
So then i understand that the form authentication that triggers from Safari/Office for Mac when accessing documents on SharePoint is expected. But when i complete the login/authentication I recieve the message: "Good work, you are authenticated". But it does not trigger the download/access to the requested document/resource. If I try to open the document one more time nothing happens.
I understand that this might not be related to the iRule at all. This could be a LTM/APM/Sharepoint 2016/Safari/Office for Mac or OS X issue. But does that make any sense to you? Appreciate any help!
And another question: Should I use Persistent Cookie on the access profile itself in addition to your iRule?
Thanks again!
- Stanislas_Piro2Cumulonimbus
@Pal: I guess you misunderstand how it works.
This irule allow to support clientless authentication according to browser / office compatibility.
Without this irule, office apps (even libreoffice) can't edit online documents. This irule allow to use such applications to edit documents online by changing authentication method.
A persistent cookie is also inserted to allow office apps to download documents without reauthentication if both browser and app accept to share cookies. Until now, only Microsoft products allow such configuration (Explorer, Edge, Office).
If Apps can't share cookies, there is no solution.
- Pål_Andre_RopstNimbostratus
Hi Stanislas,
Thank you so much for your provided iRule. This is working great. Our only problem is getting it to work with Safari on Mac OS with Office for Mac (currently testing 2016). What happens is just like you describe. When your authenticated with Safari, and then try to open a Office document within SharePoint, this trigger the Office product (word, Excel, Powerpoint). But when the office application opens this trigger the access policy/authentication process within a browser - just like you describe. In reference to your last post - does that mean that this will not work with Safari on Mac?
Are there any customization that can be done to get it to work?
Thanks again.
Sincereley, Pål Andrè
- Stanislas_Piro2Cumulonimbus
Which login pop up?
If this is a form inside a browser it means the code works fine.
If you expected a transparent authentication because of browser auth, it works only with internet explorer, and only if the url is in trusted sites.
Authentication is stored in a persistent cookie written on disk by browser, but office only read cookies in ie directories.
- JoeTheFifthAltostratus
Here is one for you guys: i don't use adfs. I use a forms login page to authenticate users in an ldap then I get the username and do a kerberos sso. This works fine with sharepoint and Office web apps but opening office documents does not work. i have the login pop. I will need to take a look a look at the irule and try ti adapt it. if you have any hints please shoot :-)