cap
cap
(collection of authentication packages) provides a collection of related
packages which enable support for OIDC, JWT Verification and Distributed Claims.
Please note: We take security and our users' trust very seriously. If you believe you have found a security issue, please responsibly disclose by contacting us at security@hashicorp.com.
Contributing
Thank you for your interest in contributing! Please refer to CONTRIBUTING.md for guidance.
oidc package
A package for writing clients that integrate with OIDC Providers. Primary types provided by the package are:
- Request
- Token
- Config
- Provider
The package also provides callbacks (in the form of http.HandlerFunc) for handling OIDC provider responses to authorization code flow (with optional PKCE) and implicit flow authentication attempts.
Example of a provider using an authorization code flow:
// Create a new provider config
pc, err := oidc.NewConfig(
"http://your-issuer.com/",
"your_client_id",
"your_client_secret",
[]oidc.Alg{oidc.RS256},
[]string{"https://your_redirect_url"},
)
if err != nil {
// handle error
}
// Create a provider
p, err := oidc.NewProvider(pc)
if err != nil {
// handle error
}
defer p.Done()
// Create a Request for a user's authorization code flow authentication attempt,
// with a 2 min timeout for completion.
oidcRequest, err := oidc.NewRequest(2 * time.Minute, "https://your_redirect_url")
if err != nil {
// handle error
}
// Create an auth URL
authURL, err := p.AuthURL(ctx, oidcRequest)
if err != nil {
// handle error
}
fmt.Println("open url to kick-off authentication: ", authURL)
Create a http.Handler for OIDC authentication response redirects.
func NewHandler(ctx context.Context, p *oidc.Provider, r callback.RequestReader) (http.HandlerFunc, error)
if p == nil {
// handle error
}
if rw == nil {
// handle error
}
return func(w http.ResponseWriter, req *http.Request) {
oidcRequest, err := rw.Read(ctx, req.FormValue("state"))
if err != nil {
// handle error
}
// Exchange(...) will verify the tokens before returning.
token, err := p.Exchange(ctx, oidcRequest, req.FormValue("state"), req.FormValue("code"))
if err != nil {
// handle error
}
var claims map[string]interface{}
if err := token.IDToken().Claims(&claims); err != nil {
// handle error
}
// Get the user's claims via the provider's UserInfo endpoint
var infoClaims map[string]interface{}
err = p.UserInfo(ctx, token.StaticTokenSource(), claims["sub"].(string), &infoClaims)
if err != nil {
// handle error
}
resp := struct {
IDTokenClaims map[string]interface{}
UserInfoClaims map[string]interface{}
}{claims, infoClaims}
enc := json.NewEncoder(w)
if err := enc.Encode(resp); err != nil {
// handle error
}
}
}
jwt package
Package jwt provides signature verification and claims set validation for JSON Web Tokens (JWT) of the JSON Web Signature (JWS) form.
JWT claims set validation provided by the package includes the option to validate all registered claim names defined in rfc7519#section-4.1.
JOSE header validation provided by the the package includes the option to validate the "alg" (Algorithm) Header Parameter defined in rfc7515#section-4.1.
JWT signature verification is supported by providing keys from the following sources:
- JSON Web Key Set (JWKS) URL
- OIDC Discovery mechanism
- Local public keys
JWT signature verification supports the following asymmetric algorithms defined in rfc7518.html#section-3.1:
Identifier | Signing Algorithm |
---|---|
RS256 | RSASSA-PKCS1-v1_5 using SHA-256 |
RS384 | RSASSA-PKCS1-v1_5 using SHA-384 |
RS512 | RSASSA-PKCS1-v1_5 using SHA-512 |
ES256 | ECDSA using P-256 and SHA-256 |
ES384 | ECDSA using P-384 and SHA-384 |
ES512 | ECDSA using P-521 and SHA-512 |
PS256 | RSASSA-PSS using SHA-256 and MGF1 with SHA-256 |
PS384 | RSASSA-PSS using SHA-384 and MGF1 with SHA-384 |
PS512 | RSASSA-PSS using SHA-512 and MGF1 with SHA-512 |
EdDSA | Ed25519 using SHA-512 |
Example usage of JWT signature verification and claims set validation using keys from a JWKS URL:
ctx := context.Background()
keySet, err := jwt.NewJSONWebKeySet(ctx, "your_jwks_url", "your_jwks_ca_pem")
if err != nil {
log.Fatal(err)
}
validator, err := jwt.NewValidator(keySet)
if err != nil {
log.Fatal(err)
}
expected := jwt.Expected{
Issuer: "your_expected_issuer",
Subject: "your_expected_subject",
ID: "your_expected_jwt_id",
Audiences: []string{"your_expected_audiences"},
SigningAlgorithms: []jwt.Alg{jwt.RS256},
}
token := "header.payload.signature"
claims, err := validator.Validate(ctx, token, expected)
if err != nil {
log.Fatal(err)
}
For additional documentation and usage examples, see jwt/README.md.