jwt
16 TopicsJSON 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.03.5KViews2likes14CommentsAPM: OAUTH2 JWT Token with groups claim
Hello and happy new year 😉 We use APM as OAuth Authorization Server to create JWT token for apps. One of our customers wants to use the MicroProfile JWT(MP-JWT) for his application, that needs some specific claims: https://github.com/eclipse/microprofile-jwt-auth/blob/master/spec/src/main/asciidoc/interoperability.asciidoc One requirement is to encode the groups claim in JSON array: "groups": ["red-group", "green-group", "admin-group", "admin"] We now try to set the claim with groups from the Active Directory. With an iRule, I filtered the AD groups (from memberOf) and set a new APM variable (session.custom.groups) with this value: ["red-group", "green-group", "admin-group", "admin"] When I now add a claim groups with %{session.custom.groups} as value, I see that string in my JWT token: "groups": "[\"red-group\", \"green-group\", \"admin-group\", \"admin\"]" So the value is escaped and has is in quotation marks. Is there any chance to send claims as JSON array? Any help would be appreciated.2.8KViews0likes25Comments3 Ways to Connect BIG-IP to Istio
Istio, a service mesh, uses “zero trust” to authenticate services.We’ll look at 3 ways to connect BIG-IP to Istio. 1. TCP The first method that we will use will be TCP.This will allow the BIG-IP to passthrough client traffic to Istio’s Ingress Gateway. 2. Mutual TLS (mTLS) The second method is to use the Client Certificate Constrained Delegation (C3D) feature of BIG-IP to authenticate client connections via mTLS and then generate a new client certificate (with similar attributes to the original) and use that newly minted certificate to authenticate to Istio. This second example is useful for scenarios where you are unable to install a trusted (externally CA signed) certificate into Istio (corporate policy prohibits it) and/or you want to establish a TLS DMZ.Despite the connection using mTLS the BIG-IP can inspect the traffic (i.e. log to Splunk), apply policy (i.e. insert XFF headers, WAF protection), etc… 3. JSON Web Tokens (JWT) Istio can use JWT tokens to authenticate users, but not all enterprise systems speak JWT.Using BIG-IP Access Policy Manager (APM) we can create an access policy that performs Single-Sign On (SSO) with an OAuth bearer token (JWT).This enables us to authenticate a client with username / password and convert the identity into a JWT token that is understood by Istio. Video Please These 3 methods are discussed and demo’d in the following YouTube video. Thanks for reading/watching!2.1KViews2likes2CommentsAPM JWT Multiple Providers NOT WORKING
Dear F5 community, Using F5 APM 16.1.3 (as an oauth resource server) I am trying to implement a per-request policy that will verify the signature of JWT tokens sent by the client. These JWT tokens can be issued from two differents issuer (Azure AD or STS). I am able to verify JWT tokens for each provider seperatly using a dedicated "JWT provider" with only one Provider attached. When using 2 providers as follow I got following error message: WWW-Authenticate:Bearer error="invalid_token",error_description="Issuer Mismatch : Claim issuer= https://sts.windows.net/ Provider issuer=https://login.microsoftonline.com/v2.0" Based on F5 doc below, the built-in object supports having multiple JWT providers https://clouddocs.f5.com/cli/tmsh-reference/v15/modules/apm/apm_oauth_jwt-provider-list.html Configuration is pretty simple: - 1 Access Policy with "Allow" all ending - 1 Per-Request Policy with "OAuth Scope" set to "Internal" with the "jwt-allowed-providers-list" I guess It is most likely a bug. Anyone was able to make it work with multiple JWT providers ? I can workaround this by parsing the JWT payload, then determining the issuer and based on the issuer make two branches in the VPE: - first branch with the "oauth scope A" that will validate the token using JWT-Provider-A - second branch with the "oauth scope B" that will validate the token using JWT-Provider-B Thanks2KViews1like5CommentsConfiguring NGINX API micro-gateway to support Open Banking's Advanced FAPI security profile
Introduction In my last article, Integrating NGINX Controller API Management with PingFederate to secure financial services API transactions, we have seen how to configure NGINX Controller to perform basic JWT authorization against PingFederate, configured as OIDC IdP / OAuth Autorization Server. One weakness of the basic JWT authentication mechanism is the lack of context: anyone presenting a valid JWT will be allowed to performed the actions granted by the token, even if the sender is not the original OAuth client that was issued the token. This opens an avenue for attackers to use JWTs stollen from their rightful owners. Ideally, a mechanism of restricting the usage of the JWT to their original requestor is needed and this type of protection is specifically required for API calls presenting the highest risk, such as financial API calls. For example, Financial-grade API (FAPI) Security Profile 1.0 - Part 2: Advanced (Read and Write API Security Profile) specifies that: Authorization server: shall only issue sender-constrained access tokens; shall support MTLS as mechanism for constraining the legitimate senders of access tokens; Uncertainty of resource server handling of access tokens The protected resources that conform to this document shall not accept a bearer access token. They shall only support sender-constrained access tokens via MTLS. It is therefore useful to examine the configuration of NGINX, in its micro-gateway deployment mode, needed to perform the function of a resource server in cases requiring the Advanced FAPI security profile. Setup A high-level diagram of the lab environment used to demonstrate this setup is found below: The roles performed by each network element are described below: Authentication and API flow The workflow is very similar with the one described in my last article, with the differences highlighted here in bold: The user logs into the Third Party Provider application ("client") and creates a new funds transfer The TPP application redirects the user to the OAuth Authorization Server / OIDC IdP - PingFederate The user provides its credentials to PingFederate and gets access to the consent management screen where the required "payments" scope will be listed If the user agrees to give consent to the TPP client to make payments out of his/her account, PingFederate will generate an authorization code (and an ID Token) and redirect the user to the TPP client The TPP client opens an MTLS connection to the IdP, authenticates itself with a client certificate, exchanges the authorization code for a user-constrained access token and attaches it as a bearer token to the /domestic-payments call sent to the API gateway over an MTLS session authenticated with the same client certificate The API Gateway terminates the MTLS session and obtains the client certificate, authenticates the access token by downloading the JSON Web Keys from PingFederate, checks the hashed client certificate matches the value found in the token and grants conditional access to the backend application The Kubernetes Ingress receives the API call and performs WAF security checks via NGINX App Protect The API call is forwarded to the backend server pod Examining the differences between the workflows, it becomes apparent the extra actions NGINX API micro-gateway has to perform to support this advanced security use case are MTLS termination and client certificate hash verification. NGINX API micro-gateway configuration The full configuration is available on DevCentral's Code Share: Configure NGINX microgateway for MTLS termination and client certificate hash verification I will highlight below the most relevant parts of the configuration. MTLS termination server { server_name api.bank.f5lab; listen 443 ssl; ssl_certificate /etc/nginx/f5lab.crt; ssl_certificate_key /etc/nginx/f5lab.key; ssl_session_cache off; ssl_prefer_server_ciphers off; ssl_client_certificate /etc/nginx/updated_ca.crt; ssl_verify_client on; ssl_verify_depth 10; A detailed explanation of each of these commands can be found in the ngx_http_ssl_module user guide. JWT client certificate hash verification To compute and validate the client certificate hash, we will use an njs script (more information on njs scripting language and installation process can be found here). The njs script used (named "x5t.js" in our case) is shown below: function validate(r) { var clientThumbprint = require("crypto") .createHash("sha256") .update(Buffer.from(r.variables.ssl_client_raw_cert.replace(/(\n|----|-BEGIN|-END| CERTIFICATE-)/gm, ''), 'base64')) .digest("base64url"); return clientThumbprint === r.variables.jwt_cnf_fingerprint ? '1' : '0'; } export default { validate } Importing the "x5t.js" script in the main nginx configuration is done by: js_import /etc/nginx/x5t.js; We are populating the value of variable $jwt_cnf_fingerprint (available to the njs script via "r.variables.jwt_cnf_fingerprint") by extracting the 'x5t#S256' value from JWT: auth_jwt_claim_set $jwt_cnf_fingerprint 'cnf' 'x5t#S256'; The "validate" function of "x5t.js" will the compare the value of $jwt_cnf_fingerprint variable extracted from JWT with the computed SHA256 hash of the client certificate and set the validation result in the $thumbprint_match variable. js_set $thumbprint_match x5t.validate; Lastly, we will make a decision to accept or block client's access based on the validation result: if ($thumbprint_match != 1) { return 403 'Access denied because client SSL certificate thumbprint does not match jwt_cnf_fingerprint'; } Conclusion Supporting MTLS termination and client certificate hash validation against sender-constrained JWTs issued by Authorization Servers such as PingFederate, enables NGINX API micro-gateway to support Open Banking's Advanced FAPI security profile. Resources The UDF lab environment used to build this configuration can be found here.1.9KViews1like0CommentsiRule - jwt is generated prior to authentication
Hoping you guys could shed some light on this, all our efforts have failed so far Scenario: Client hits https://service.com/example Initial uri is stored in an sessions variable called session.server.landinguri Client is redirected to IdP(F5 SAML federation with IDP) Authentication takes place and if completed the client is redirected to the landinguri and a jwt is signed and generated via an iRule (signature, username etc) jwt is passed to the URI (yes, the applications requires this. HTTP header via authorization header is not supported) We have tried generating the jwt in the APM but are unable to decrypt it in to proper format for appending to the URI. This is why we are doing this in an iRule Our problem is that the iRule jwt is being generated at the start of the APM in the initial session BEFORE the authentication is taking place which results in e.g an empty username being displayed. We have been experimenting withACCESS_POLICY_AGENT_EVENT but cant get things to work as it still picks up the jwt that is generated prior to SAML authentication. When debugging we can see 3 jwts being generated in the flow, the first one with an empty username, the following 2 (after successful auth) contain the correct info. Any advice on troubleshooting this is highly appreciated!Solved1.6KViews0likes2Commentsoauth server generated jwt token problem
Hi all, We have a customer try to do oauth with a dovecot server, they have the following problems using the f5 as a oauth server: The "typ" jwt header is missing, this should be set to "JWT". F5 set the JWT token nbf (not valid before) to some minutes in the past, this breaks dovecot auth. Customer want to use the following oauth features, are these supported? https://openid.net/specs/openid-connect-frontchannel-1_0.html https://openid.net/specs/openid-connect-backchannel-1_0.html Do you know how the above could be customized in f5 to set to values the dovecot would accept? Thank you for any hint. Peter1.2KViews3likes5CommentsF5 Oauth server introspect JWT access token from external server
dear all, I already have setup a F5 as oauth client, F5 as oauth server (AS) and F5 as API gateway where F5 performs the introspect internally in its oauth database. So that is all working fine. Now we would like to perform introspect from an external server / API gateway towards the F5 and we are using JWT access tokens generated by F5 oauth server. I would assume the endpoint is /f5-oauth2/v1/introspect and we should define resource-server-id, resource-server-secret and access-token. According to the F5 documentation it is used only for Opaque tokens but that is not recommended as best practice is to use JWT. /f5-oauth2/v1/introspect as token introspection endpoint for validating Opaque tokens Now the question, how am I able to perform introspect from an external API server towards the F5 oauth server to validate that the provided JWT access token is still valid?1.1KViews0likes1CommentAuthorization Header Defined as Unparsable by F5_ASM
One of our development teams is adding a new OAUTH token feature to an application. Sending the JSON call with the Authorization header creates an error within F5_ASM (ver 15.13): HTTP ValidationUnparsable request content Details Unparsable authorization header value I also see that the Authorization header's data is masked, though there's no settings for the Authorization header in the policy. What are my best options for troubleshooting this issue?898Views0likes3CommentsF5 OAUTH JWT error "failed trust verification with trusted CA bundle"
Hello to All, I tried using F5 as an OAUTH server that generates JWT tokens on 16.1.3.2 but I get the error "01071ca5:3: The JWK config (/Common/F5-CA) associated to OAuth profile (/Common/F5-Oauth-Server-JWT) failed trust verification with trusted CA bundle (/Common/clientCA-cert). " . I generated my own CA cert following "K14499: Using OpenSSL to create CA and client certificates (11.x - 16.x" https://support.f5.com/csp/article/K14499 Maybe I am doing something wrong or it is a bug but I am not certain as I do not have not worked with F5 as an Oauth JWT server. I am checking just really fast with the community as this is not something critical but if someone has seen this I will be happy to get a feedback 🙂 With or without Certificate Chain it is the same error or X5C. I am wondering as the certificate is 2048 bits RSA ifit should be 256 but I think this shouldn't matter. As a workaround I am using Octed JWT Key with a long shared password generated by a password generator that works. Also I tested with opaque keys as shown in https://support.f5.com/csp/article/K14391041 and it works but with those keys I do not think there is a way the F5 Auth server to return some usefull info like AD groups/emails to to the Oauth Client that is another F5 device as with JWT this is done with Claims. I did not find if F5 as an Oauth Authorization server supports UserInfo Request URI where after the authorization code that the web browsers provide is exchanged for Opaque Access token to configure what info the F5 Oauth Server to provide to the F5 Oauth Client but maybe some knows this. To bad that for OpenID Connect the JWT needs first to be enabled for the Oauth profile and thisagain means using Octed JWT Keys for either Access or ID tokens 😞699Views0likes2Comments