The OAuth 2.0 device authorization grant (RFC 8628) as a small CLOS protocol.
The library defines a device authentication client with injected transport, clock, sleep, and browser effects, a credential store protocol, unverified JWT claim readers, and exact-string secret redaction. Providers subclass the RFC 8628 client to validate provider-specific token claims, resolve account identity, and publish credentials through their own stores. Proprietary device flows can subclass the base client and reuse the same transport and failure protocol.
- ASDF system:
cl-rfc8628 - Test system:
cl-rfc8628/tests - Package:
CL-RFC8628 - Nickname:
RFC8628
Install the locked dependencies and run all tests:
./script/bootstrap
./script/check(defclass portal-client (rfc8628:rfc8628-device-authentication-client)
())
(rfc8628:device-authentication-login
(make-instance 'portal-client
:issuer "https://portal.example"
:client-id "public-client-id"
:device-code-path "/oauth2/device/code"
:token-path "/oauth2/token"
:scope "openid offline_access"
:request-function #'rfc8628:device-authentication-request
:poll-function #'rfc8628:rfc8628-device-authentication-poll-for-tokens)
manager)DEVICE-AUTHENTICATION-LOGIN requests a device code, displays the
verification URL and user code, optionally opens the browser, polls
through authorization_pending and slow_down responses, validates the
token document, and publishes OAUTH-CREDENTIALS through the manager’s
primary source. Failures signal DEVICE-AUTHENTICATION-ERROR carrying a
stage, an optional HTTP status, and a redacted OAuth error code.
RFC8628-DEVICE-AUTHENTICATION-AUTHORIZATION-CLASSnames the pending authorization class.RFC8628-DEVICE-AUTHENTICATION-VALIDATE-TOKEN-RESPONSErejects token documents missing provider-specific claims.RFC8628-DEVICE-AUTHENTICATION-ACCOUNT-IDresolves the stable account identity, by default from the OpenID or access token subject.RFC8628-DEVICE-AUTHENTICATION-PUBLISH-CREDENTIALSpersists approved credentials, by default through the primary source.
*SECRET-REGION-FUNCTION*wraps every code path holding secret material, for hosts with credential-scoping machinery.*USER-AGENT-FUNCTION*names the client on the wire.*DEVICE-AUTHENTICATION-ERROR-CLASS*substitutes a condition subclass joining the host’s own hierarchy.
Implement CREDENTIAL-SOURCE-LOAD, CREDENTIAL-SOURCE-SAVE,
CREDENTIAL-SOURCE-PATHNAME, and CREDENTIAL-SOURCE-LABEL for private
storage. Loads must reread current storage. Implement durable atomic
publication in the source; managed acquisition ignores the save result.
CREDENTIAL-MANAGER is the minimal store protocol for device flows.
Subclass MANAGED-CREDENTIAL-MANAGER to add credential acquisition and
refresh coordination. Supply :primary-source and, optionally,
:bootstrap-source. Override CREDENTIAL-MANAGER-REFRESH-EXCHANGE to
exchange a refresh token. Return two values: refreshed credentials and
whether to publish them. Return false for the second value when another
process already published the rotation. Name the service and login action
with CREDENTIAL-MANAGER-PROVIDER-LABEL and CREDENTIAL-MANAGER-LOGIN-HINT.
Use CALL-WITH-CREDENTIALS or WITH-CREDENTIALS to acquire credentials
and invoke a callback inside *SECRET-REGION-FUNCTION*. Acquisition rereads
the primary source, imports only a valid bootstrap access token when the
primary source is absent, and refreshes expiring credentials. Concurrent
refresh callers share one exchange and one immutable success or failure.
An abandoned leader releases waiters to retry. Coordination is local to
one manager in one SBCL process; source implementations own cross-process
serialization. Managers retain paths and account identity, not tokens.
CREDENTIAL-MANAGER-ACCEPT-ACCOUNT pins the first account and rejects
changes unless called with :allow-change t. After a provider-specific
reused-token error, call CREDENTIAL-MANAGER-NEWER-ROTATION with the
attempted token to reread and validate a newly stored rotation.
Failures signal CREDENTIAL-ERROR, CREDENTIALS-UNAVAILABLE, or
TOKEN-REFRESH-FAILED. Hosts can select matching condition classes with
*CREDENTIAL-ERROR-CLASS*, *CREDENTIALS-UNAVAILABLE-CLASS*, and
*TOKEN-REFRESH-FAILED-CLASS*. Preserve their initialization arguments
and exclude secret-bearing values from errors returned by an exchange.
COLL-Attribution. See LICENSE.lisp.