An OIDC authorization server building blocks with security and privacy by design philosophy.
This will not provide a full-featured standalone OIDC Server but a limited and secure settings according to your use cases :
online usersusingauthorization_codeflow with mandatory PKCE via Pushed Authorization Request with state enforcement;machine-to-machineusingclient_credentialsbased on asymetric authentication schemes;devices and constrained environments, you know for IO(v)T (Internet Of vulnerable Thing);offline usersusingrefresh_tokenflow for application that need toact as an online user but without its online interaction.impersonation / delegationusingtoken_exchangeflow fro resource server who wants to access authenticated external resource on behalf of the subject with a restricted resource level privilege set.
I have been developing OAuth/OIDC/UMA providers since 2012, in multiple
languages and environments. People generally don't understand OIDC flows.
It's like driving a car that requires you to know how engine work and how the car is built. But the only thing you want is to drive your car.
OAuth / OIDC is often criticized in favor of SAML, but implementations are more vulnerables than the protocol itself. OAuth is just offered as a developer framework, but it's true to say that not all developers are aware of security problems.
Implementations are done by developers that don't have/take the time to browse the specification maze, they read them quickly with their own belief in mind. As a consequence the specifications are not understood but barely interpreted, that will produce faulty implementations.
Also security products are often associated with NIH syndrom.
What I observed in real life:
- Not using
authorization_codebecause it doesn't have user/password in the flow; client_credentialsgrant type to be used ascustomer credentialslikepasswordgrant type but for external customer user access (login form with client credentials);- Using
client_credentialsfrom a JS public UI (hardcoded client_secret); - Dynamic authorization application based on token claims without signature checks;
- Authentication based on the fact the you can retrieve the token ... not validating token content (Token is here => You are admin);
Many OIDC providers give you a lot of features that you have to understand and choose to maximize your security posture. So that your security posture is correlated to your understanding of OAuth and OIDC and their implementations in the product.
I don't like this idea to be honest.
I understand the requirements of commercial products to have a wide compatibility
matrix, but by allowing insecure settings for one client you can compromise the
the whole platform, and also lose the customer inside the feature fog.
But OAuth / OIDC specification are only tools in a toolbox, and they need to be orchestrated in a proper way to provide a simple, efficient and secure service.
That's the reason why I've started this project as an OSS project, to provide a simple and solid implementations of 4 OAuth flows.
- Enforce OIDC features as a complete suite according to selected use-case;
- Provide a complete toolchain to enforce security and privacy without the complete knowledge of all related protocols;
- Enhance security posture based on security objectives not the understand of security protocols;
- Provide a battle-tested framework;
- Provide a wire protocol decoupled framework, OIDC is tighly coupled to HTTP but it can be easily decoupled to become portable between other wire protocols (CoAP);
- A complete OIDC compliant server. By making some optional and recommended
parameters as required,
solidcan't pass the OIDC compliance tests;
I made sample server and various integrations inside examples/ folder.
See examples/README.md for how to run them; every
example directory also carries a README.md with a mermaid sequence diagram
describing the exact flow its main.go implements.
PAR+DPoP+JARMis enabled and enforced forauthorization_codeflow;hybridflow is not and will be supported; Web applications must use server side component (or lambda) to negociate authorizations; By design, your client-side application code (JS) should not be exposed until you are identified;- Only response_type
codewill be supported to enforce server-side negociation; PKCE+Nonceis enforced by default for all client types duringauthorization_codeflow;authorization_codeflow could not be started by theuser-agent, as the default behavior, theclientmust use PAR protocol to retrieve arequest_urithat will qualify and start theauthorization_codeflow;- Asymetric authentication methods are enforced by default;
- No
HSxxx/RSxxxsupport as JOSE signature algorithms;HSxxxdoesn't provide digital signature;RSxxxuses RSA algorithms that needs to have high computation to improve security protection level so that it will be more difficult for constrained environment (IoT) to have same security protection level as a normal application;- Only
elliptical curvesinvolved algorithms will be used;
access_token/refresh_tokenarehybridtokens so that they embed protocol validation details (expiration, etc.) without any privacy related info (sub). These informations are referenced via an embededjticlaim that will address an AS-only accessbile record that will contains extra data;audienceparameter is mandatory for request that needscopein order to target the corresponding application. This will allow various validations betweenclientandapplication, andconsentmanagement;PARmust use JWT encoded request payload to due request registration;- Application-type profiles (
server/profile) constrain clients whoseapplication_typemaps to a strict profile entry — grant types, response types and token-endpoint authentication methods per profile (web,native,device,service;browserdeliberately excluded). Enforcement lives in the shared HTTP layer (server/httpkit) so every assembly inherits it; clients without a profile-known application type fall back to their registration metadata. - Token serialization is an assembler decision: the same transport-agnostic
services mint opaque verifiable reference tokens (signed UUIDv7), JWTs,
HPKE-encrypted JWTs, or RFC 8392 CWTs — see
sdk/tokenand theSOLID_EXAMPLE_TOKEN_FORMATswitch inexamples/coapace. - Syntactic request validation (required fields, length bounds, charset
patterns) is expressed as
buf.validateannotations on the protobuf domain model (proto/oidc/**) and enforced by a protovalidate first level in every service; semantic rules stay in business logic.
- OAuth Core
- draft-ietf-oauth-v2-1-16 - The OAuth 2.1 Authorization Framework — vendored (
docs/rfcs/draft-ietf-oauth-v2-1-16.txt), core requirements enforced and adversarially tested (integration/oauth21_adversarial_test.go) - RFC 9700 - OAuth 2.0 Security Best Current Practice — implemented and adversarially tested (
integration/) - draft-ietf-oauth-security-topics-update-03 - Updates to OAuth 2.0 Security Best Current Practice — vendored and adversarially tested (
integration/securities_adversarial_test.go); AS-side audience hardening (§2.1) and client-side issuer-identifier audience (§2.1.2.1) applied
- draft-ietf-oauth-v2-1-16 - The OAuth 2.1 Authorization Framework — vendored (
- OAuth Extensions
-
Discovery
-
Identity authentication
-
Cross-App Access / identity chaining
- RFC8693 - OAuth 2.0 Token Exchange —
token_exchangegrant withmay_actgating,actdelegation-chain preservation (fail-closed depth cap), and confirmation (cnf) key binding carried over secure-compared - draft-ietf-oauth-identity-chaining-17 - OAuth 2.0 Identity Chaining — identity-chained access tokens minted via token exchange;
act/may_actsemantics adversarially tested (integration/identity_chaining_act_test.go) - draft-ietf-oauth-identity-assertion-authz-grant-04 - OAuth 2.0 Identity Assertion Authorization Grant (Cross-App Access) — ID-JAG issuance at the IdP AS, redemption by the Resource AS via the JWT Bearer grant (
sdk/idjag,server/services/token); serialization-format agnostic, pairwise subject identifiers, adversarially tested (integration/idjag_adversarial_test.go)
- RFC8693 - OAuth 2.0 Token Exchange —
-
Client authentication
- Asymmetric authentication
- RFC7523 - JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants
- RFC7521 - Assertion Framework for OAuth 2.0 Client Authentication and Authorization Grants
-
private_key_jwt- https://oauth.net/private-key-jwt/ -
attest_jwt_client_auth- OAuth 2.0 Attestation-Based Client Authentication — header transport (OAuth-Client-Attestation/OAuth-Client-Attestation-PoP) with attestationtyp: oauth-client-attestation+jwtand PoPtyp: oauth-client-attestation-pop+jwt(draft-11 §§4, 5.1), registry-pinned Client Attester trust,cnfprivate-material rejection,client_id↔subbinding (§7.5), jti-burn replay protection (§12.1); adversarially tested (integration/attestation_adversarial_test.go, demo client inexamples/attestationclient) -
spiffe_jwt/spiffe_wit/spiffe_x509- OAuth SPIFFE Client Authentication — all three SVID credential types (JWT-SVID viaclient_assertion_type: ...jwt-spiffe, WIT-SVID via the attestation headers, X.509-SVID via mutual TLS); trust-domain signing keys resolved exclusively fromBundleSources keyed by trust domain (draft §6: static pre-configured bundles or SPIFFE bundle endpoints with refresh-hint polling,sdk/spiffe), never from SVID issuer claims (§8.1); fail-closedspiffe_idclient binding with/*path-segment wildcards (§5.1); implemented and adversarially tested (integration/spiffe_test.go, demo client inexamples/spiffeclient) -
tls_client_auth- RFC8705 - OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens — PKI mutual-TLS client authentication (§2.1): the TLS peer certificate is matched fail-closed against exactly one registered subject binding (subject_dn,san_dns,san_uri,san_ipbinary-compared,san_email;server/clientauthentication/tls_client_auth.go); certificate-bound tokens via thex5t#S256cnfmember (§3.1,sdk/token), enforced at the refresh grant (§7.1) and the example resource server; RFC Appendix A fixture-anchored and adversarially tested (integration/rfc8705_mtls_test.go)
- Asymmetric authentication
-
Grant Types
-
client_credentialsgrant type -
authorization_codegrant type- RFC7636 - Proof Key for Code Exchange by OAuth Public Clients - https://oauth.net/2/pkce/
- RFC9126 - OAuth 2.0 Pushed Authorization Requests (PAR) - https://oauth.net/2/pushed-authorization-requests/
- RFC9101 - The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR) (JAR)
- JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)
- RFC9207 - OAuth 2.0 Authorization Server Issuer Identification
- RFC9396 - OAuth 2.0 Rich Authorization Requests (
authorization_detailscarried in JAR request objects; deep-equal subset narrowing at the token endpoint; fail-closed forclient_credentials/token_exchange)
-
refresh_tokengrant type - RFC8628 -
urn:ietf:params:oauth:grant-type:device_codegrant type —expired_token/access_denied/slow_downsemantics all enforced server-side - rfc8628 - RFC7523 - JWT Bearer grant —
urn:ietf:params:oauth:grant-type:jwt-bearer(server/services/token/grant_jwt_bearer.go); used to redeem ID-JAG assertions at the Resource AS -
urn:openid:params:grant-type:cibagrant type — OpenID Connect Client Initiated Backchannel Authentication Flow (poll delivery mode;binding_messagepromoted to required; signed request objects verified against client JWKS; optionaldpop_jktsession binding with enforced proof-of-possession at the token endpoint — RFC 9449 §10;authorization_detailsfixed at bc-authorize time; no refresh tokens,offline_accessstripped — RFC 9700 §4.12.2) — adversarial tests inintegration/ciba_adversarial_test.go
-
-
Resource
-
Client
- RFC7591 - OAuth 2.0 Dynamic Client Registration — minimal defensive dynamic client registration via the gRPC
ClientRegistrationService(server/services/clientregistration): asymmetric auth methods only (no client secrets issued),coderesponse type only, implemented-grant allowlist, loopback-redirect exception, operator-gated - RFC7592 - OAuth 2.0 Dynamic Client Registration Management Protocol — Read/Update/Delete via the gRPC
ClientRegistrationManagementService(server/grpckit): per-client registration access token issued at registration time is the sole management credential (invalid-token attempts revoke it), updates are full replacement re-validated with the RFC 7591 rules, delete removes the client and revokes every issued token; no client secrets - (DRAFT) OAuth Client ID Metadata Document
- OAuth 2.0 Client ID Scheme
- RFC7591 - OAuth 2.0 Dynamic Client Registration — minimal defensive dynamic client registration via the gRPC
-
Tokens
- Privacy
- Pairwise subject identifier
- RFC 9901 - Selective Disclosure for JSON Web Tokens (SD-JWT) —
sdk/sdtokencore (wire-agnostic disclosure forgery/processing) with the JWT serialization insdk/sdtoken/sdjwt: compact-serialization SD-JWT/SD-JWT+KB issuer/holder/verifier, recursive disclosures, decoy digests, mandatory key binding posture; vendored (docs/rfcs/rfc9901.txt); adversarially tested (integration/sdjwt_adversarial_test.go) - draft-ietf-spice-sd-cwt-08 - Selective Disclosure CBOR Web Tokens (SD-CWT) —
sdk/sdtoken/sdcwt: COSE_Sign1 SD-CWT/KBT issuer/holder/verifier withredacted_claim_keys(simple 59) / tag 60 elements, nesting and decoy validation, definite-length and duplicate-map-key enforcement; adversarially tested (integration/sdcwt_adversarial_test.go)
- Scheme
- RFC6750 - The OAuth 2.0 Authorization Framework: Bearer Token Usage
- RFC7800 - Proof-of-Possession Key Semantics for JSON Web Tokens (JWTs)
- RFC9449 - OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP)
- RFC8705 - OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens — certificate-bound tokens (
x5t#S256cnfmember, §3.1); client authentication (tls_client_auth/self_signed_tls_client_auth) listed under Client authentication
- Authentication by reference
- Random string
- Verifiable token (signed UUID)
- Authentication by value
- RFC7519 - JSON Web Token (JWT)
- RFC8392 - CBOR Web Token (CWT) — COSE_Sign1 signers/verifiers (
sdk/token/cwt, elliptic-curve + ML-DSA algorithms only, RFC 8392 §7.1 claim keyasint map); wired as an alternative token generator in the CoAP/ACE example (SOLID_EXAMPLE_TOKEN_FORMAT=cwt) and round-trip tested through the real token service (integration/cwt_token_test.go) - draft-ietf-jose-hpke-encrypt-22 - Use of HPKE with JWE — token encryption strategy
sdk/token/hpke(Integrated + Key Encryption modes, stdlibcrypto/hpke); HPKE-encrypted JWT access/refresh tokens inexamples/authorizationserver; RFC conformance via draft Appendix A vectors (sdk/token/hpke/testdata/jose-vectors.json); adversarially tested (integration/jose_hpke_test.go) - draft-ietf-cose-hpke-27 - Use of HPKE with COSE — CWT token encryption strategy
sdk/token/cwt(CoseHPKEEncrypter/CoseHPKEKeyEncryptionEncrypter/CoseHPKEVerifier, Integrated + Key Encryption modes, stdlibcrypto/hpke); draft conformance via section 5 examples; both strategies share the wire-agnostic ciphersuite registry and key conversion ofsdk/hpke(JWE labels + COSE identifiers)
- Privacy
-
Token Management
-
Constrained Environments (ACE)
- RFC 9200 - Authentication and Authorization for Constrained Environments (ACE-OAuth) —
application/ace+cborwire codec (sdk/ace), CBOR-abbreviated token/introspection payloads and AS Request Creation Hints; full CoAP triangle demo (examples/coapace, adversarially tested inintegration/ace_adversarial_test.go) - RFC 9201 - COSE Profile of ACE — COSE_Key confirmation members (cnf) for PoP keys
- RFC 9202 - DTLS Profile of ACE —
coap_dtlsprofile: mutual DTLS 1.2 client authentication and certificate-bound tokens - RFC 8747 - Proof-of-Possession Key Semantics for CBOR Web Tokens (CWTs) — cnf confirmation members (COSE_Key by value, kid by reference) in the ACE codec
- RFC 9200 - Authentication and Authorization for Constrained Environments (ACE-OAuth) —
-
- HTTP
- Authorization Server
- Standalone
- Shared HTTP building blocks (
server/httpkit: RFC 6749/9126/8414 handlers, client-authentication and security-headers middleware, profile enforcement) — assembled by the reference exampleexamples/authorizationserver - Caddy plugin
- Reverse Proxy
- Caddy plugin
- Authorization Server
- CoAP
- Authorization Server
- Standalone (RFC 9200 ACE-OAuth over mutual DTLS 1.2 —
examples/coapace,sdk/aceCBOR wire codec; coap_dtls profile per RFC 9202)
- Standalone (RFC 9200 ACE-OAuth over mutual DTLS 1.2 —
- Authorization Server
- AWS
- Auhtorization Server
- AWS Lambda
- Auhtorization Server
- OAuth 2.0
- OAuth 2.0 Client Authentication
- RFC 9700 - OAuth 2.0 Security Best Current Practice
- The standard texts of the implemented RFCs (6749, 7009, 7521, 7523, 7636, 7662, 8392, 8414, 8693, 8705, 8747, 9101, 9126, 9200, 9201, 9202, 9207, 9396, 9449, 9700, 9728, 10027) and drafts (draft-ietf-oauth-v2-1-16, draft-ietf-oauth-client-id-metadata-document-02, draft-ietf-oauth-identity-assertion-authz-grant-04, draft-ietf-oauth-identity-chaining-17, draft-ietf-oauth-security-topics-update-03, draft-ietf-oauth-spiffe-client-auth-02) are vendored under
docs/rfcs/as the source of truth for conformance and adversarial testing. - OpenID Connect Client-Initiated Backchannel Authentication Flow (CIBA) Core 1.0 — vendored as
docs/rfcs/openid-client-initiated-backchannel-authentication-core-1_0.txt - OAuth SPIFFE Client Authentication — SPIFFE workload identity (SVIDs) as OAuth client credentials
- SPIFFE — Secure Production Identity Framework For Everyone (SPIFFE IDs, trust domains, SVIDs, bundle endpoints)
- OAuth 2.0 for Browser-Based Apps
- Financial-grade API - Part 1: Read-Only API Security Profile
- Financial-grade API - Part 2: Read and Write API Security Profile
- PKCE vs. Nonce: Equivalent or Not?
- An Extensive Formal Security Analysis of the OpenID Financial-grade API
- Mix-Up, Revisited
- Financial-grade API: JWT Secured Authorization Response Mode for OAuth 2.0 (JARM)