Thanks to Arran for his hints on calling a REST API from FreeRADIUS. rlm_rest is working, doing authentication for WPA2-Personal (PAP via captive portal) and WPA2-Enterprise Wi-Fi clients (PEAP/MSCHAPv2). Using FreeRADIUS 3.0.16 (Ubuntu 18.04). PAP is handled in the default site, while the MSCHAP2 part of PEAP/MSCHAP2 is handled in the inner-tunnel. The trick is preventing the REST API from being called in the default site with EAP outer messages, which would result in 9 unwanted API calls. The authorize REST API code does pre-authentication and if it passes it returns the NT-Password or Cleartext-Password as appropriate. To find out if the user was authenticated, a post-auth REST API is used to log success/failure. rest module configuration: authorize { uri = "RADIUS_API_PATH/radius?action=authorize" method = 'post' body = 'json' auth = 'basic' username = 'RADIUS_API_USER' password = 'RADIUS_API_PASSWORD' } post-auth { uri = "RADIUS_API_PATH/radius?action=post-auth&result=%{control:Post-Auth-Type}" # result is either '' or 'Reject' ... } API pseudo code for authorize: if pre-authenticated if mschap return { "control:NT-Password": {"type":"octets","value":[ "0x0123456789ABCDEF0123456789ABCDEF" ]}}, 200 else return { "control:Cleartext-Password": {"type":"string","value":[ "clear-text-password-here" ]}}, 200 else: return {}, 401 default site config: authorize { mschap eap { ok = return } if (!ok && !updated) { # Only call rest if not handled by EAP rest } pap } post-auth { ... if (!&reply:EAP-Message) { # Only call rest if not EAP rest } ... } inner-site config: authorize { mschap eap { ok = return } if (!ok && !updated) { # Only call rest if not already handled rest } pap } post-auth { ... rest ... } Issues: When doing PEAP/MSCHAPv2, the authorize REST API gets called twice in the inner-tunnel, for two different EAP messages. Wastes a few milliseconds, but only a minor issue. Using radtest -t mschap valid-user@domain invalid_password 127.0.0.1 0 radius_secret replied with MS-CHAP-Error = "\000E=691 R=1 C=ad8367a70f809d72 V=2" My reading of the MS-CHAP-V2 RFC2759 and PPP CHAP RFC1994 is that this should have been MS-CHAP-Error = "E=691 R=1 C=ad8367a70f809d72 V=2"