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- Stanislas_Piro2Cumulonimbus
Hi Kai,
I tried in version tcl 8.4 et tcl 8.5, and I confirm
is optimized in version 8.5. but[info exists]
has also poor performance in tcl 8.4.$last_ua_agent ne [set last_ua_agent XXX]
$ tclsh8.5 % set ua "Mozilla" Mozilla % set authschema "browser" browser % time { expr { [info exists authschema] } } 100000 0.32418845 microseconds per iteration % time { expr { $ua != [ set ua "Mozilla"] } } 100000 0.56738861 microseconds per iteration $ tclsh8.4 % set ua "Mozilla" Mozilla % set authschema "browser" browser % time { expr { [info exists authschema] } } 100000 0.98919 microseconds per iteration % time { expr { $ua != [ set ua "Mozilla"] } } 100000 0.73574 microseconds per iteration
I understand the user agent can change on a single TCP connection, but the client type may not change (As I know, proxy servers does not reuse TCP connection for different client connections).
thank you very much for pointing me which part of this irule I can optimize, and share your experience about irule and APM interoperability.
I hope this feature will be included in future sharepoint iapp and deployment guide.
Next step will be to add Microsoft ADAL support with future version if F5 oauth implementation will be compatible.
Hi Stanislas,
has a rather poor performance on TCL8.4. With TCL8.5 the performance will become much better. In addition I also tried to cover the scenario, that the UA may change on a single TCP connection. But a good catch that I could combine the two[info exists]
's into a single one. This will save some additional cycles... Thanks! 😉[if]
Cheers, Kai
- Stanislas_Piro2Cumulonimbus
Kai,
instead of creating a variable with previous user agent, you can check if the variable authschema exists
if { [info exists authschema] } then { Do nothing, keep previous request authschema value } elseif { ( [HTTP::header value "X-FORMS_BASED_AUTH_ACCEPTED"] equals "t" ) or ( [HTTP::header value "X-FORMS_BASED_AUTH_ACCEPTED"] equals "f" ) } then { Enumerate explicit MS-OFBA authentication capabilities Background: https://msdn.microsoft.com/en-us/library/office/cc313069(v=office.12).aspx Explicit MSOFBA support detected. set authschema "ms-ofba" } else { Enumerate implicit MS-OFBA authentication capabilities Background: https://msdn.microsoft.com/en-us/library/office/cc313069(v=office.12).aspx switch -glob -- [string tolower $last_ua_agent] "*office protocol discovery*" - \ "*microsoft office*" - \ "*microsoft data access internet publishing provider*" - \ "*non-browser*" - \ "msoffice 12*" - \ "*microsoft-webdav-miniredir*" - \ {*ms frontpage 1[23456789]*} { Implicit MSOFBA support detected. set authschema "ms-ofba" } "*ms frontpage*" { Legacy client detected set authschema "legacy" } "*mozilla*" - \ "*opera*" { Regular web browser detected. set authschema "browser" } default { Unknown user-agent detected. set authschema "legacy" } }
- Stanislas_Piro2Cumulonimbus
and I confirm I had some issues about ACCESS::session with more than 30K access session on BIGIP 10250.
BIGIP 10250 use 12 TMM process, I had some request dropped when access::session did not receive session status from all tmm.
In the first version, I used ACCESS::session to get session status, get session variable (for one customer, MRHSession_SP cookie is only sent if the user check "private computer" in the logon page).
I changed the irule to limit ACCESS::session command to optimize the code.
- Stanislas_Piro2Cumulonimbus
the use of
is not a big issue.[ACCESS::user getsid $user_key]
in ACCESS_SESSION_STARTED, the session uuid is changed from default tmm.policyname.logonname to username."md5 of user password"
if { [ info exists user_key ] } { ACCESS::session data set "session.user.uuid" $user_key }
so, when a new request comes with same username and password and without session cookie, APM will not create a new session if a previous one exists with the same username / password.
This is first created for active sync to prevent multiple access session for one user.
in the active sync, there is a option to insert in the hash the client ip address to prevent a user with multiple devices to share the same access session (this is the default behavior for active sync). this can be added in the irule.
Hi Stanislas,
good point to allow the MRHSession_SP persistent cookie logins only to non-browsers. I've already changed my code to include this additional security mechanism (see below). The performance impact off this change shouldn't be that much, since the
event will inject a MRHSession cookie for the very next request.HTTP_RESPONSE
I don't have any information that ACCESS::session will park the connection till every other TMM has been contacted. I thought its more like a
call where just a specific TMM (data owner) will be contacted if needed. SOL12962 does also not explain this behavior...[table]
"Note: When you run the ACCESS::session command, iRule execution on the connection will be suspended until the operation completes only if the session database record is held by another TMM; this situation allows the current TMM to retrieve the data from the other TMM before processing the remainder of the iRule. The ACCESS:: commands are available only if your BIG-IP system is licensed for the BIG-IP APM system."
Do you have additional information on the ACCESS:: connection parking behavior?
when CLIENT_ACCEPTED { set inject_session_cookie 0 } when HTTP_REQUEST { Check if APM session cookie is present and valid if { ( [set sessionid [HTTP::cookie value "MRHSession"]] ne "" ) and ( [ACCESS::session exists -state_allow $sessionid] ) } then { Allow the successfully pre authenticated request to pass } else { Enumerate explicit MS-OFBA authentication capabilities Background: https://msdn.microsoft.com/en-us/library/office/cc313069(v=office.12).aspx if { ( [HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "t" ) or ( [HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "f" ) } then { Explicit MSOFBA support detected. set authschema "ms-ofba" } else { Enumerate implicit MS-OFBA authentication capabilities switch -glob -- [string tolower [HTTP::header "User-Agent"]] "*office protocol discovery*" - \ "*microsoft office*" - \ "*microsoft data access internet publishing provider*" - \ "*non-browser*" - \ "msoffice 12*" - \ "*microsoft-webdav-miniredir*" - \ {*ms frontpage 1[23456789]*} { Implicit MSOFBA support detected. set authschema "ms-ofba" } "*ms frontpage*" { Legacy client detected set authschema "legacy" } "*mozilla*" - \ "*opera*" { Regular web browser detected. set authschema "browser" } default { set authschema "legacy" } } if { not ( $authschema eq "browser" ) and ( [set sessionid [HTTP::cookie value "MRHSession_SP"]] ne "" ) and ( [ACCESS::session exists -state_allow $sessionid] ) } then { Restore APM session cookie value HTTP::cookie insert name "MRHSession" value $sessionid set inject_session_cookie 1 Allow the successfully pre authenticated request to pass } else { if { $authschema eq "ms-ofba" } then { Send a MSOFBA compatible Access Denied response if { [HTTP::path] ne "/sp-msofba-form" } then { HTTP::respond 403 -version "1.1" \ content "Access Denied. Make sure that your client is correctly configured. See https://support.microsoft.com/en-us/kb/932118 for further information." \ noserver \ "Content-Type" "text/html" \ "X-FORMS_BASED_AUTH_REQUIRED" "https://[getfield [HTTP::host] ":" 1]/sp-msofba-form" \ "X-FORMS_BASED_AUTH_RETURN_URL" "https://[getfield [HTTP::host] ":" 1]/sp-msofba-completed" \ "X-FORMS_BASED_AUTH_DIALOG_SIZE" "800x600" \ "Set-Cookie" "MRHSession=deleted;path=/;secure" \ "Set-Cookie" "LastMRH_Session=deleted;path=/;secure" \ "Set-Cookie" "MRHSession=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;secure" \ "Set-Cookie" "LastMRH_Session=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;secure" } } elseif { $authschema eq "legacy" } then { Send a regular Access Denied response HTTP::respond 403 content "Access denied. An unsupported client access has been detected." } else { Let the regular web browser request pass to the APM policy } } } } when ACCESS_ACL_ALLOWED { switch -glob -- [string tolower [HTTP::path]] "/sp-msofba-form" { Successfully APM authenticated request MS-OFBA request detected. Redirect to MS-OFBA return URL ACCESS::respond 302 noserver Location "/sp-msofba-completed" } "/sp-msofba-completed" { Successfully APM authenticated request MS-OFBA request detected. Sending MS-OFBA return response ACCESS::respond 200 content "AuthenticatedGood Work, you are Authenticated" noserver } "*/signout.aspx" { SharePoint SignOut signature detected. Disconnect session and redirect to APM logout Page ACCESS::respond 302 noserver Location "/vdesk/hangup.php3" } "/_layouts/accessdenied.aspx" { SharePoint AccessDenied signature detected. if { [string tolower [URI::query [HTTP::uri] loginasanotheruser]] equals "true" } then { SharePoint LoginAsAnotherUser request detected. Killing the APM session an sending redirect to www-root. ACCESS::session remove ACCESS::respond 302 noserver Location "/" return } } default { Let the authenticated request pass } } when HTTP_RESPONSE { if { [HTTP::header "Content-Type" ] contains "text/html" } then { Insert persistent APM session cookie into HTTP response. HTTP::header insert "Set-Cookie" "MRHSession_SP=$sessionid;Path=/;Secure;HttpOnly" HTTP::cookie expires "MRHSession_SP" 120 relative } if { $inject_session_cookie } then { Insert APM session cookie into HTTP response. HTTP::header insert "Set-Cookie" "MRHSession=$sessionid;Path=/;Secure;HttpOnly" set inject_session_cookie 0 } }
Note: If the F5 exchange irule uses
to offload authentication request, then its a even a bigger issue... 😞 The problem is, that you can easily bruteforce AD accounts if the offloading cache is not protected by an account lockout mechanism. You can guess passwords even if the account is already locked out and if the correct password is found you are allowed to enter or at least get a slightly different error message. A max session timeout period of 1 hour wouldn't make it better if SmartPhones polls the mailbox 24/7, isn't it?[ACCESS::user getsid $user_key]
Cheers, Kai
- Stanislas_Piro2Cumulonimbus
Hi Kai,
the use of
is from F5 exchange irule. this irule is also used when enabling exchange profile.[ACCESS::user getsid $user_key]
this is based on a user key from username and password. if you think it is insecure, we can set a the variable session.max_session_timeout to 1 hour. it will force a new authentication every hour if the user changed his password or if the account is locked. this can be seamless as the authentication header is inserted in every request.
The main reason I filter on MRHSession_SP cookie only for non browser is to secure persistent cookie usage.
When managing persistent cookie for non browser, it will allow only non-browser to recover session cookie from sharepoint cookie. when user close the browser without logout, next user on a shared computer can access previous user session if persistent cookie is not expired.
if you manage the MRHSession_SP cookie for all user agents, the user will be allowed to close the browser, open again and access to sharepoint.
another reason I put ACCESS::session command only for clientless and OFBA clients is to solve performance issues.
ACCESS::session may be used carefully because it generate a pause in execution of irule waiting all other TMM answer about this session. executing this for every requests may cause latency. that's why I filtered first on user-agent, then on session status.
I understand that in my irule, there are static objects I can remove and fix OFBA urls and persist cookie name.
I agree your switch command to filter user-agents is more optimized (with frontage filter version), but I will keep irule architecture to stay generic for every client type.
I will update my irule soon with good points I found in yours.
Hi Stanislas,
In a native ADFS-based SharePoint collaboration scenario you cannot use AD fallbacks. The SAML user accounts are most likely stored in many decentralized repositories, without a direct trust nor network access to them.
My goal was to get everything working without using the AD fallback at all. Well, I had to skip the support for outdated MS Office products and changed the handling of Microsoft WebDav Clients slightly. In addition I've restructured the code here and there to reduce the complexity and increase the performance...
when CLIENT_ACCEPTED { set inject_session_cookie 0 } when HTTP_REQUEST { Check if APM session cookie is present and valid if { ( [set sessionid [HTTP::cookie value "MRHSession"]] ne "" ) and ( [ACCESS::session exists -state_allow $sessionid] ) } then { Allow the successfully pre authenticated request to pass } else { Check if persistent APM session cookie is present and valid if { ( [set sessionid [HTTP::cookie value "MRHSession_SP"]] ne "" ) and ( [ACCESS::session exists -state_allow $sessionid] ) } then { Restore APM session cookie value HTTP::cookie insert name "MRHSession" value $sessionid set inject_session_cookie 1 Allow the successfully pre authenticated request to pass } else { Enumerate explicit MS-OFBA authentication capabilities Background: https://msdn.microsoft.com/en-us/library/office/cc313069(v=office.12).aspx if { ( [HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "t" ) or ( [HTTP::header "X-FORMS_BASED_AUTH_ACCEPTED"] equals "f" ) } then { Explicit MSOFBA support detected. set authschema "ms-ofba" } else { Enumerate implicit MS-OFBA authentication capabilities switch -glob -- [string tolower [HTTP::header "User-Agent"]] "*office protocol discovery*" - \ "*microsoft office*" - \ "*microsoft data access internet publishing provider*" - \ "*non-browser*" - \ "msoffice 12*" - \ "*microsoft-webdav-miniredir*" - \ {*ms frontpage 1[23456789]*} { Implicit MSOFBA support detected. set authschema "ms-ofba" } "*ms frontpage*" { Legacy client detected set authschema "legacy" } "*mozilla*" - \ "*opera*" { Regular web browser detected. set authschema "browser" } default { set authschema "legacy" } } if { $authschema eq "ms-ofba" } then { Send a MSOFBA compatible Access Denied response if { [HTTP::path] ne "/sp-msofba-form" } then { HTTP::respond 403 -version "1.1" \ content "Access Denied. Make sure that your client is correctly configured. See https://support.microsoft.com/en-us/kb/932118 for further information." \ noserver \ "Content-Type" "text/html" \ "X-FORMS_BASED_AUTH_REQUIRED" "https://[getfield [HTTP::host] ":" 1]/sp-msofba-form" \ "X-FORMS_BASED_AUTH_RETURN_URL" "https://[getfield [HTTP::host] ":" 1]/sp-msofba-completed" \ "X-FORMS_BASED_AUTH_DIALOG_SIZE" "800x600" \ "Set-Cookie" "MRHSession=deleted;path=/;secure" \ "Set-Cookie" "LastMRH_Session=deleted;path=/;secure" \ "Set-Cookie" "MRHSession=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;secure" \ "Set-Cookie" "LastMRH_Session=deleted; expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/;secure" } } elseif { $authschema eq "legacy" } then { Send a regular Access Denied response HTTP::respond 403 content "Access denied. An unsupported client access has been detected." } else { Let the regular web browser request pass to the APM policy } } } } when ACCESS_SESSION_STARTED { if { [HTTP::cookie value "SAML_Realm"] ne "" } then { ACCESS::session data set "session.irule.realmcookie" [HTTP::cookie value "SAML_Realm"] } } when ACCESS_POLICY_COMPLETED { if { [set realm_cookie [ACCESS::session data get "session.irule.setrealmcookie"]] ne "" } then { ACCESS::respond 302 "Location" "[ACCESS::session data get "session.server.landinguri"]" "Set-Cookie" "SAML_Realm=$realm_cookie;path=/;secure" } else { ACCESS::respond 302 "Location" "[ACCESS::session data get "session.server.landinguri"]" } } when ACCESS_ACL_ALLOWED { switch -glob -- [string tolower [HTTP::path]] "/sp-msofba-form" { Successfully APM authenticated request MS-OFBA request detected. Redirect to MS-OFBA return URL ACCESS::respond 302 noserver Location "/sp-msofba-completed" } "/sp-msofba-completed" { Successfully APM authenticated request MS-OFBA request detected. Sending MS-OFBA return response ACCESS::respond 200 content "AuthenticatedGood Work, you are Authenticated" noserver } "*/signout.aspx" { SharePoint SignOut signature detected. Disconnect session and redirect to APM logout Page ACCESS::respond 302 noserver Location "/vdesk/hangup.php3" } "/_layouts/accessdenied.aspx" { SharePoint AccessDenied signature detected. if { [string tolower [URI::query [HTTP::uri] loginasanotheruser]] equals "true" } then { SharePoint LoginAsAnotherUser request detected. Killing the APM session an sending redirect to www-root. ACCESS::session remove ACCESS::respond 302 noserver Location "/" return } } default { Let the authenticated request pass } } when HTTP_RESPONSE { if { [HTTP::header "Content-Type" ] contains "text/html" } then { Insert persistent APM session cookie into HTTP response. HTTP::header insert "Set-Cookie" "MRHSession_SP=$sessionid;path=/;secure" HTTP::cookie expires "MRHSession_SP" 120 relative } if { $inject_session_cookie } then { Insert APM session cookie into HTTP response. HTTP::header insert "Set-Cookie" "MRHSession=$sessionid;path=/;secure" set inject_session_cookie 0 } }
Note: Your Clientless_Mode support can be easily included in the provided script. You just need to insert your existing code into the
script block and add the other iRule events. But be aware that the query toelseif { $authschema eq "legacy" } then {
exposes some security risks, since it can be used to bypass active account lockouts.[ACCESS::user getsid $user_key]
Update: Changed the session cookie injection mechanism.
Cheers, Kai
- Stanislas_Piro2Cumulonimbus
Hi Kai,
This irule is working on VS with following access profiles:
- SAML SP with SAML IdP with fallback to AD Auth for non MSOFBA clients (session.clientless variable condition to choose authentication method)
- Standard Logon Page / AD Auth
- Standard Logon Page / AD Auth with Captcha
If you write an irule to improve APM / sharepoint compatibility, please share it.
Hey Stanislas,
I'm currently in the process in writing an iRule for ADFS protected SharePoints. Recycled some parts of your iRule and optimized some parts here and there. Also found some interesting tweaks for native Windows WebDav Client. Will send you the outcome once finished... ;-)
Cheers, Kai