oauth
4 TopicsBypass Azure Login Page with OAuth login_hint on F5 BIG-IP APM
Overview This article demonstrates how to enhance the user experience between F5 BIG-IP APM (OAuth Client) and Azure AD (OAuth Authorization Server) by implementing the login_hint parameter. This approach eliminates the need for users to enter their credentials twice, once on the F5 login page and again on the Azure login page. Problem Statement When users access applications protected by F5 APM that authenticate against Azure AD using OAuth/OIDC, they often encounter a suboptimal experience: User enters credentials on the F5 login page (often using their familiar sAMAccountName) F5 redirects to Azure AD for OAuth authentication Azure AD presents another login page asking for the same credentials (expecting UPN/email format) This creates additional friction because users may not know their User Principal Name (UPN) and typically use their sAMAccountName for domain authentication. The login_hint parameter solves this by allowing F5 APM to translate the user's sAMAccountName to their UPN and pre-populate the Azure login page, effectively bypassing the redundant login prompt. Alternative approach The same functionality can be achieved using SAML, as described here. However, I find the OAuth approach cleaner, as it eliminates the need for an iRule and also allows you to include the prompt parameter. This can be useful when you want to force authentication in Azure instead of relying on an existing session. See herefor details. Prerequisites F5 BIG-IP with APM Azure AD tenant with application registration Basic OAuth/OIDC configuration between F5 and Azure AD (as outlined in https://my.f5.com/manage/s/article/K53313351) Implementation Steps Step 1: Complete Basic OAuth Setup Follow the standard F5 APM OAuth configuration with Azure AD: Configure OAuth Server Object Navigate to Access ›› Federation ›› OAuth Client/Resource Server ›› OAuth Server Create a new OAuth server configuration for Azure AD Set the provider type to "Microsoft Identity Platform 2.0" Configure Access Profile Create an access profile with OAuth Client agent Configure authentication redirect and token requests Step 2: Create Custom Authentication Redirect Request The key modification involves creating a custom authentication redirect request that includes the login_hint parameter: Navigate to Request Configuration Access ›› Federation ›› OAuth Client/Resource Server ›› Request Clone the Default Request Find the existing /Common/MSIdentityPlatform2.0AuthRedirectRequest Create a copy Name it something descriptive like MSIdentityPlatform2.0AuthRedirectRequest_LoginHint and the same parameters as /Common/MSIdentityPlatform2.0AuthRedirectRequest Add login_hint Parameter Parameter Name: login_hint Parameter Type: custom Parameter Value: %{session.ad.last.attr.userPrincipalName} or any other variable containing user’s UPN based on your environment and Authentication configuration In the Parameters section, click Add Configure the new parameter: Step 3: Update VPE OAuth Configuration Access the Visual Policy Editor Go to Access ›› Profiles/Policies ›› Access Profiles (Per-Session Policies) Edit your access profile Click Edit to open the VPE Modify OAuth Client Agent Locate the OAuth Client agent in your policy Edit the OAuth Client agent properties In the Authentication Redirect Request dropdown, select your new custom request: MSIdentityPlatform2.0AuthRedirectRequest_LoginHint Apply Access Policy Click Apply Access Policy to save changes Step 4: Session Variable Configuration and UPN Translation The key to successful login_hint implementation is ensuring the proper session variable is populated with a UPN-formatted email address. F5 APM can automatically translate sAMAccountName to UPN during AD authentication. Complete Policy Flow Example Start ↓ Logon Page (user enters: DOMAIN\john.doe or john.doe) ↓ AD Auth (authenticate and populate session variables) ↓ AD Query (to fetch user attributes) ↓ OAuth Client (with custom redirect request including login_hint=john.doe@company.com) ↓ Azure AD (pre-populated with UPN, bypasses login prompt) ↓ Application Access Troubleshooting Common Issues login_hint Parameter Not Working Verify the session variable contains a valid email address Check that the custom request is selected in the OAuth Client agent Ensure the parameter type is set to "custom" Session Variable Empty or Wrong Format Verify AD authentication and query occurs before OAuth redirect Check AD attribute mapping configuration Confirm the userPrincipalName attribute exists in your AD schema Ensure domain suffix matches Azure AD tenant domain Azure AD Still Prompts for Login Verify the email format matches Azure AD expectations Check Azure AD application configuration for login_hint support Ensure the user exists in the Azure AD tenant Debugging Tips Enable APM debug logging: tmsh modify sys db log.apm.level value debug Use browser developer tools to inspect the OAuth redirect URL Verify session variables using Variable Assign agents in VPE Security Considerations The login_hint parameter only pre-populates the username field; users still must provide valid credentials This is a user experience enhancement, not a security bypass Ensure session variables don't contain sensitive information beyond the username/email Conclusion Implementing login_hint with OAuth on F5 BIG-IP APM significantly improves user experience by eliminating redundant login prompts. The key advantage is that F5 APM can seamlessly translate users' familiar sAMAccountName credentials to the UPN format required by Azure AD, allowing users to authenticate once with their domain credentials while Azure AD receives the properly formatted UPN for the login_hint. This approach maintains security while providing a seamless user experience, particularly beneficial in environments where: Users are more familiar with their sAMAccountName than their UPN Organizations want to minimize authentication friction Azure itself is also federated to another IDP and you want transparent rederiction The solution leverages F5 APM's AD integration capabilities to handle the username format translation automatically, making it transparent to end users.428Views3likes2CommentsJSON Web Token (JWT) Parser
Problem this snippet solves: This feature is now native in v13.1 and it is strongly recommended you implement the native solution instead. This code is left only as an example for future use cases, it should not be used for JWT handling because there is no signature validation. This code parses a JWT (JSON Web Token) received by a Big-IP acting as an OAuth client and creates session variables for the JSON parameters in the header and payload. Example use cases might include Azure AD B2C or Azure AD Enterprise integration. This iRule does not perform signature validation. Code from the "Parse and Set Session Variables" section down could be easily harvested for other JSON parsing use cases that do not need the JWT decoding. How to use this snippet: Attach this iRule to the virtual server receiving the JWT that is configured for OAuth. Inside the VPE after the OAuth Client agent add an iRule agent with id jwt-parse. This iRule will set several variables including: session.oauth.jwt.last.header session.oauth.jwt.last.payload session.oauth.jwt.last.signature In addition it will create a session variable for each parameter in the header and payload in the following syntax. session.oauth.jwt.header.last.* session.oauth.jwt.payload.last.* You can then call these session variables elsewhere. Code : when ACCESS_POLICY_AGENT_EVENT { if { [ACCESS::policy agent_id] eq "jwt-parse" } { #log local0. "JWT-Parse: Started" #Get the JWT set jwt [ACCESS::session data get -secure session.oauth.client.last.id_token] #log local0. "JWT-Parse: JWT Received - jwt is $jwt" #Separate the header, payload, and signature set jwt_header [getfield $jwt "." 1] ACCESS::session data set session.oauth.jwt.last.header $jwt_header #log local0. "JWT-Parse: Header extracted - jwt_header and session.oauth.jwt.last.header are $jwt_header" set jwt_payload [getfield $jwt "." 2] ACCESS::session data set session.oauth.jwt.last.payload $jwt_payload #log local0. "JWT-Parse: Payload extracted - jwt_payload and session.oauth.jwt.last.payload are $jwt_payload" set jwt_signature [getfield $jwt "." 3] ACCESS::session data set session.oauth.jwt.last.signature $jwt_signature #log local0. "JWT-Parse: Signature extracted - jwt_signature and session.oauth.jwt.last.signature are $jwt_signature" #Base 64 decode the header and payload #Fix encoding issues in header set jwt_header_modified $jwt_header set tail [string length $jwt_header_modified] if {$tail % 4 == 2} { append jwt_header_modified {==} } elseif {$tail % 4 == 3} { append jwt_header_modified {=} } #log local0. "JWT-Parse: Header encoding fixes complete - jwt_header_modified is $jwt_header_modified" #Fix encoding issues in payload set jwt_payload_modified $jwt_payload set tail [string length $jwt_payload_modified] if {$tail % 4 == 2} { append jwt_payload_modified {==} } elseif {$tail % 4 == 3} { append jwt_payload_modified {=} } #log local0. "JWT-Parse: Payload encoding fixes complete - jwt_payload_modified is $jwt_payload_modified" #Base64 decode set jwt_header_modified [b64decode $jwt_header_modified] #log local0. "JWT-Parse: Header Base 64 decoded - jwt_header_modified is $jwt_header_modified" set jwt_payload_modified [b64decode $jwt_payload_modified] #log local0. "JWT-Parse: Payload Base 64 decoded - jwt_payload_modified is $jwt_payload_modified" #Parse and Set Session Variables #Remove JSON characters set jwt_header_modified [string map {\{ {} \} {} \[ {} \] {} \" {}} $jwt_header_modified] #log local0. "JWT-Parse: Header JSON Characters removed - jwt_header_modified is $jwt_header_modified" set jwt_payload_modified [string map {\{ {} \} {} \[ {} \] {} \" {}} $jwt_payload_modified] #log local0. "JWT-Parse: Payload JSON Characters removed - jwt_payload_modified is $jwt_payload_modified" #Split into field/value pairs set jwt_header_modified [split $jwt_header_modified ,] #log local0. "JWT-Parse: Header Fields split - jwt_header_modified is $jwt_header_modified" set jwt_payload_modified [split $jwt_payload_modified ,] #log local0. "JWT-Parse: Payload Fields split - jwt_payload_modified is $jwt_payload_modified" #Set APM session variables for each header parameter foreach parameter $jwt_header_modified { set variable_name [getfield $parameter ":" 1] set variable_value [getfield $parameter ":" 2] ACCESS::session data set session.oauth.jwt.header.last.$variable_name $variable_value #log local0. "JWT-Parse: Header session variable set - session.oauth.jwt.header.last.$variable_name is $variable_value" } #Set APM session variables for each payload parameter foreach parameter $jwt_payload_modified { set variable_name [getfield $parameter ":" 1] set variable_value [getfield $parameter ":" 2] ACCESS::session data set session.oauth.jwt.payload.last.$variable_name $variable_value #log local0. "JWT-Parse: Payload session variable set - session.oauth.jwt.payload.last.$variable_name is $variable_value" } } } Tested this on version: 13.04.1KViews2likes14Comments