AuthorizationCodeAffinity

AuthorizationCodeAffinity

The main reason why SAML relies primarily on the front-channel for SSO despite some additional security risks is that it makes deployability better over time by a couple orders of magnitude. The ability to freely deploy changes or new software versions internally and test against essentially any SP through simple /etc/hosts file manipulation is a massive benefit that is practically mandatory if you care about IdP reliability over time at all.

The reliance on a back-channel breaks this completely without additional features and deployment steps. V3.1 of the OP introduces a small change that allows for some of this testing capability to be restored with some (non-trivial) additional work on load balancer configuration.

The property idp.oidc.nodePrefix can be set to assign a specific tag to a particular server node. Typically this would be done via a dedicated property file, to avoid accidentally duplicating the value on two servers. The value must be no longer than 10 characters.

If set, authorization codes and opaque access/refresh tokens issued by the OP will be prefixed with a signal value and the node prefix value (padded to 10 if required). A load balancer with sufficient flexibility (like most) can be configured to recognize specific prefix values in the queries or bodies of requests and direct them to a specific server. Doing this allows a “staging” deployment server to prefix its codes and tokens such that requests from clients can be routed back to that server, ideally while not routing any other requests to it. This allows the staging server to be exercised against applications without generally impacting other users, as is commonly done with SAML.

Note that the tokens can be decrypted by any server with the shared key, as before, so if a mistake is made and a client connects to the wrong server, it should not result in failures strictly due to inability to unwrap the data, though it will warn about the issue.

 

Examples

F5 BIG-IP loadbalancer iRule

There are TODOs in the script that you need to adapt to your environment

This iRule assumes that idp.oidc.nodePrefix matches the name of the node in the loadbalancer.

when CLIENT_ACCEPTED { # TODO: You need to configure logging here # https://clouddocs.f5.com/api/irules/HSL.html # https://clouddocs.f5.com/api/irules/HSL__open.html set hsl [HSL::open -proto UDP -pool syslog_server_pool] set static::debug 0 } # https://cdn.f5.com/product/bugtracker/ID931149.html proc resolv_ptr_v4 { addr_v4 } { # Convert $addr_v4 into its constituent bytes set ret [scan $addr_v4 {%d.%d.%d.%d} a b c d] if { $ret != 4 } { return } # TODO: You probably need/want to configure this DNS setting too # Perform a PTR lookup on the IP address $addr_v4, and return the first answer set ret [RESOLVER::name_lookup "/Common/f5-aws-dns" "$d.$c.$b.$a.in-addr.arpa" PTR] set ret [lindex [DNSMSG::section $ret answer] 0] if { $ret eq "" } { # log local0.warn "DNS PTR lookup for $addr_v4 failed." return } # Last element in '1.1.1.10.in-addr.arpa. 600 IN PTR otters.example.com' return [lindex $ret end] } proc set_pool_member { hsl node_name } { foreach { pool_member } [members -list [LB::server pool]] { set pool_member_ip [lindex $pool_member 0] set rrs [call resolv_ptr_v4 $pool_member_ip] if {[string first $node_name $rrs] > 0} { if { $static::debug } { HSL::send $hsl "<190> token-affinity: $pool_member named $node_name matches $rrs" } pool [LB::server pool] member $pool_member_ip return } } } when HTTP_REQUEST { # Check if Authorization header exists if {[string equal [HTTP::uri] "/idp/profile/oidc/token"] and [string equal [HTTP::method] "POST"]} { if {[regexp -nocase {code=PXR5([^X]{1,10})} [HTTP::payload] match node_name]} { if { $static::debug } { HSL::send $hsl "<190> token-affinity: Extracted node name from POST token $node_name" } call set_pool_member $hsl $node_name } } elseif {[HTTP::header exists "Authorization"]} { HSL::send $hsl "<190> token-affinity: [HTTP::uri] has headers [HTTP::header names]" set auth_header [HTTP::header "Authorization"] # Extract the bearer token (assumes "Bearer <token>" format) if {[regexp -nocase {^Bearer\s+PXR5([^X]{1,10})} $auth_header match node_name]} { # Log for debugging if { $static::debug } { HSL::send $hsl "<190> token-affinity: Extracted node name from Bearer token $node_name" } call set_pool_member $hsl $node_name } else { if { $static::debug } { HSL::send $hsl "<190> token-affinity: Invalid Authorization header format" } } } }