Authentication Flow¶
FadianRoam uses 802.1X enterprise Wi-Fi authentication with federated RADIUS proxying.
Protocol Stack¶
┌─────────────────────────────────┐
│ Wi-Fi Supplicant │ User device (laptop, phone)
├─────────────────────────────────┤
│ EAP-TTLS / PAP │ Outer: TLS tunnel; Inner: plaintext password
├─────────────────────────────────┤
│ RADIUS (UDP 1812) │ Authentication protocol
├─────────────────────────────────┤
│ Keycloak ROPC │ Identity Provider validation
└─────────────────────────────────┘
Authentication Steps¶
Local User (Same Site)¶
A user connecting at their home site:
sequenceDiagram
participant D as Device
participant AP as Wi-Fi AP
participant R as RADIUS Server
participant K as Keycloak IDP
D->>AP: 802.1X EAP-Start
AP->>R: Access-Request (user@realm.a)
R->>R: Realm matches local → strip realm
R->>K: ROPC grant (username, password)
K-->>R: Token (success) or 401 (fail)
R-->>AP: Access-Accept / Access-Reject
AP-->>D: Wi-Fi connected / denied
- Device initiates 802.1X authentication
- AP forwards EAP to local RADIUS server
- RADIUS identifies
@realm.aas local realm - RADIUS strips realm suffix, sends credentials to Keycloak via ROPC
- Keycloak validates and returns result
- RADIUS sends Access-Accept or Access-Reject
Roaming User (Different Site)¶
A user from Site B connecting at Site A:
sequenceDiagram
participant D as Device
participant AP as Site A AP
participant RA as Site A RADIUS
participant RELAY as Federation Relay
participant RB as Site B RADIUS
participant KB as Site B Keycloak
D->>AP: 802.1X EAP-Start
AP->>RA: Access-Request (user@realm.b)
RA->>RA: Realm not local → proxy
RA->>RELAY: Proxy via MGMT VPN
RELAY->>RELAY: Lookup realm.b → Site B
RELAY->>RB: Forward via MGMT VPN
RB->>KB: ROPC grant (username, password)
KB-->>RB: Token (success)
RB-->>RELAY: Access-Accept
RELAY-->>RA: Access-Accept
RA-->>AP: Access-Accept
AP-->>D: Wi-Fi connected
- Device initiates 802.1X at Site A
- Site A RADIUS sees
@realm.b— not a local realm - Site A RADIUS proxies request to the Federation Relay over MGMT VPN
- Relay looks up
realm.bin its realm table and forwards to Site B's RADIUS - Site B RADIUS validates against its own Keycloak
- Result flows back through the chain
EAP Configuration¶
Supported Methods¶
| Method | Support | Notes |
|---|---|---|
| EAP-TTLS/PAP | Required | Primary method, simplest Keycloak integration |
| PEAP/MSCHAPv2 | Optional | Requires password hash storage in Keycloak |
| EAP-TLS | Optional | Certificate-based, no password needed |
Why EAP-TTLS/PAP?¶
EAP-TTLS creates a TLS tunnel between the device and the RADIUS server. Inside this tunnel, credentials are sent as plaintext (PAP). This is secure because:
- The outer TLS tunnel encrypts everything over the air
- The RADIUS server decrypts and forwards to Keycloak via localhost
- Keycloak receives a standard ROPC (Resource Owner Password Credentials) grant
- No password hashing scheme compatibility issues
TLS Certificate¶
Each member's RADIUS server needs a valid TLS certificate for the EAP outer tunnel:
- Must be issued by a publicly trusted CA (Let's Encrypt, ZeroSSL, etc.)
- Self-signed certificates cause connection failures on most devices
- Certificate CN/SAN should match the member's RADIUS domain
- Auto-renewal is strongly recommended
Keycloak Integration¶
ROPC Flow¶
FreeRADIUS communicates with Keycloak using the Resource Owner Password Credentials grant:
POST /realms/{realm}/protocol/openid-connect/token
Content-Type: application/x-www-form-urlencoded
client_id=freeradius
&client_secret=<secret>
&grant_type=password
&username=<stripped-username>
&password=<user-password>
&scope=openid
- Success: Keycloak returns an access token → RADIUS sends Access-Accept
- Failure: Keycloak returns 401 → RADIUS sends Access-Reject
Realm Separation¶
Each member operates an independent Keycloak realm for roaming users:
- Realm is separate from any other services the member runs
- No user synchronization between members
- Each member controls their own user lifecycle (registration, suspension, deletion)
Realm Naming¶
Each member registers a unique realm identifier with the federation:
- Format:
roam.example.netormember-name.fadianroam.net - Must be a valid DNS-style identifier
- Registered in the federation configuration repository
- Used for RADIUS realm routing (e.g.,
user@roam.example.net)
Realm Uniqueness
Realm names must be globally unique within the federation. The Federation Relay uses the realm to route authentication requests to the correct member.