Giter Club home page Giter Club logo

cap's Introduction

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 [email protected].

Contributing

Thank you for your interest in contributing! Please refer to CONTRIBUTING.md for guidance.


Go Reference

A package for writing clients that integrate with OIDC Providers. Primary types provided by the package are:

  1. Request
  2. Token
  3. Config
  4. 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
        }
    }
}

Go Reference

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.


Go Reference

ldap is a package for writing clients that authenticate using Active Directory or LDAP.

Primary types provided by the package:

  • ldap.Client
  • ldap.ClientConfig

Example usage

An abbreviated example of authenticating a user:

client, err := ldap.NewClient(ctx, &clientConfig)
if err != nil { 
  // handle error appropriately
}

// authenticate and get the user's groups as well.
result, err := client.Authenticate(ctx, username, passwd, ldap.WithGroups())
if err != nil { 
  // handle error appropriately
}

if result.Success {
  // user successfully authenticated...
  if len(result.Groups) > 0 {
    // we found some groups associated with the authenticated user...
  }
}

Go Reference

A package for writing clients that integrate with SAML Providers.

The SAML library orients mainly on the implementation profile for federation interoperability (also known as interoperable SAML), a set of software conformance requirements intended to facilitate interoperability within the context of full mesh identity federations. It supports the Web Browser SSO profile with HTTP-Post and HTTP-Redirect as supported service bindings. The default SAML settings follow the requirements of the interoperable SAML deployment profile.

Example usage

    // Create a new saml config providing the necessary provider information:
    cfg, err := saml.NewConfig(<entityID>, <acs>, <metadata>, options...)
	// handle error

    // Use the config to create the service provider:
    sp, err := saml.NewServiceProvider(cfg)
    // handle error

    // With the service provider you can create saml authentication requests:

    // Generate a saml auth request with HTTP Post-Binding
    template, err := sp.AuthRequestPost("relay state", options...)
    // handle error

    // Generate a saml auth request with HTTP Request-Binding
    redirectURL, err := sp.AuthRequestRedirect("relay state", options...)
    // handle error

    // Parsing a SAML response:
    r.ParseForm()
    samlResp := r.PostForm.Get("SAMLResponse")

    response, err := sp.ParseResponse(samlResp, "Response ID", options...)
    // handle error

You can find the full demo code in the saml/demo package.

cap's People

Contributors

austingebauer avatar benashz avatar dependabot[bot] avatar hashicorp-copywrite[bot] avatar hashicorp-tsccr[bot] avatar hcjulz avatar jasonodonnell avatar jefferai avatar jimlambrt avatar johnlanda avatar louisruch avatar nodyhub avatar raymonstah avatar remilapeyre avatar swenson avatar tbalasavage avatar xaevman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cap's Issues

Custom claims extraction

Is your feature request related to a problem? Please describe.
I have an access token with a custom claim that is messy to extract because of its nested structure, for example:

map[aud:example default_user_id:<nil> exp:%!s(float64=1.669480533e+09) iat:%!s(float64=1.669307733e+09) iss:example:[map[customer_id:example customer_name:example role:example user_id:example]] sid:example sub:example ui_token:map[access_token:example created_at:2022-11-24T16:35:30Z customer_id:example expires_at:2022-11-25T04:35:30Z id:example name:example browser session scope:global service_id:<nil> services:[] token_type:bearer updated_at:2022-11-24T16:35:30Z user_id:example]]

I'd like to get to the access_token key inside the ui_token map.

Describe the solution you'd like
A function I could use under the jwt package to easily extract a key from the claim.

Describe alternatives you've considered
Just manually checking each field and coercing from an interface{} type to the required types. It's not the end of the world, but it feels like something that should be made easier through some kind of package abstraction.

Add support to skip issuer validation.

the coreos package that cap uses recently added support to allow the validation of the issuer against the discovery doc: coreos/go-oidc#315

upgrading to v3 of go-oidc and supporting a similar flag in cap would allow callers to use providers like Azure with it's non-compliant issues.

The id_token declaration contains multiple audience(`aud`), without authorized party (`azp`), this situation will always be wrong

Describe the bug
When I use Vault to pass OIDC certification, The returned id_token claim contains multiple audiences(aud), without authorized party (azp), which resulted in an error output. Provider. VerifyIDToken: invalid id_token : multiple audiences and authorized party (%!s(<nil>)) is not equal client_id (vault-oidc-client-id) , I traced back to this by looking at the relevant source code, hair this code, there is a simple logic problem.

To Reproduce
Steps to reproduce the behavior:

  1. vualt oidc config
vault server -dev -dev-root-token-id=myroot -log-level=debug > /tmp/vault.log 2>&1 & 
sleep 1 
export VAULT_TOKEN=myroot 
export VAULT_ADDR=http://127.0.0.1:8200 

cat - > /tmp/devpolicy. hcl <<EOF 
path "/secret/*" { 
 capabilities = ["read", "list"] 
} 
EOF 
vault policy write dev /tmp/devpolicy.hcl 

vault auth enable oidc 

vault write auth/oidc/config \ 
    oidc_discovery_url="http://localhost:8082/api/oauth2" \ 
    oidc_client_id="vault-oidc-client-id" \ 
    oidc_client_secret="admin" \ 
    default_role="demo" 

vault write auth/oidc/role/demo \ 
    bound_audiences="vault-oidc-client-id" \ 
    allowed_redirect_uris="http://127.0.0.1:8200/ui/vault/auth/oidc/oidc/callback" \ 
    allowed_redirect_uris="http://127.0.0.1:8250/oidc/callback" \ 
    user_claim="sub" \ 
    policies=dev 

Expected behavior
If the ID Token contains multiple audiences, the Client should verify that an azp Claim is present

reference

Screenshots

  • id_token (This is the id_token returned by OIDC OP, parsed through jwt.io
id_token = eyJhbGciOiJSUzI1NiIsImtpZCI6Ik5UQXhabU14TkRNeVpEZzNNVFUxWkdNME16RXpPREpoWldJNE5ETmxaRFUxT0dGa05qRmlNUSIsInR5cCI6IkpXVCJ9.eyJhdF9oYXNoIjoiTGtobjVWbWFHeHd6TldBVGNhRVhidyIsImF1ZCI6WyJodHRwczovL215LWNsaWVudC5teS1hcHBsaWNhdGlvbi5jb20iLCJ2YXVsdC1vaWRjLWNsaWVudC1pZCJdLCJhdXRoX3RpbWUiOjE2NTkyNTE2MzcsImV4cCI6MTY1OTI3MzIzNywiaWF0IjoxNjU5MjUxNjQ1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODIvYXBpL29hdXRoMiIsImp0aSI6ImYwYmE2NTE4LTUxZTktNDA4My04ZDhmLThlOTViOWYzNTI1ZiIsIm5vbmNlIjoibl92cEVVckdLYXpsZlk0Wm5aNXBmMiIsInJhdCI6MTY1OTI1MTYzNywic3ViIjoicGV0ZXIifQ.LOZUPJb_C3MbOXIjjMk8509SAHwAIot-VjztKkIbkplHe0FHfNOIbijx8HfyGURzqNNSgvmvta-57jdL5XVJZQzBZ4TDihUjyEzOPr-cdeZMla3FpwZRC7ftIUzuNxB4-ntfT0_5_LWVoVfC32B5cnxrxxYuVQNB-B8gz5-5ZE9K6e6W-s3x-7ltPIex4XJlNOM8JVjghJEH_381zUFKu2_sD-PjON1sHzgVeLHcX_WbomztKm6ZUhn-DOPiTtIsAwyFNGARUT80WJ-LOuFa7uD0Rvun0Jjs0OC6ReJ9rGad_z4F3rIm7lNQH4PDCkUuUe_aEYFfJJjO2NTog63b2g

image

  • vault
    image

Desktop (please complete the following information):

  • OS: win10
  • Browser: chrome
  • Version: 103.0.5060.134

Option to relax bound_issuer validation (domain vs URL)

Is your feature request related to a problem? Please describe.
GitLab is in the process of modifying the JWT tokens it provides in CI jobs, and in particular it is changing the iss (Issuer) claim value:

  • it was gitlab.something.com in old-style tokens (deprecated but widely used)
  • it is https://gitlab.something.com in new tokens (obtained via a new pipeline keyword)

It's a legitimate change, but transition is painful in the context of GitLab/Vault interactions, because on Vault side it requires two JWT auth URLs; one for each style of tokens (for each specific bound_issuer). GitLab users (pipelines authors) must take into account that when they update the way they obtain a JWT token, they must also adapt their target Vault auth URL. In our case it will affect many people across many project/teams.

Describe the solution you'd like
I'd like to have the option to configure JWT auth plugin only once in Vault, in such a way it tolerates both forms of Issuer claim (FQDN and https://FQDN). It could be a boolean option to enable the special case (something like "ignore protocol in iss: claim if bound_issuer is a domain name"), or the ability to provide a list of bound_issuers.
At least the "special case" option would not be too difficult to take into account in cap/jwt, I think, here:

cap/jwt/jwt.go

Line 141 in 6d1e00a

if expected.Issuer != "" && expected.Issuer != claims.Issuer {

Describe alternatives you've considered
The alternative is to live with two JWT auth URL in Vault during the transition period, until deprecated tokens disappear, like this.

Boundary 0.2.1 doesn't parse Dex OIDC provider's `aud` claim

[Note: this issue was seen in Boundary, but is being filed here per conversation with @jimlambrt]

Describe the bug
Boundary cannot unmarshal the aud claim that Dex returns. The output given in the Boundary UI is {"kind":"Internal", "message":"authmethod_service.(Service).authenticateOidcCallback: Callback validation failed.: parameter violation: error #100: oidc.Callback: unable to get user info from provider: unknown: error #0: Provider.UserInfo: failed to parse claims for UserInfo verification: json: cannot unmarshal string into Go struct field verifyClaims.Aud of type []string"}

To Reproduce
I set up a Dex provider in a Docker container with the following config:

  • Docker run:

docker run -d -v /etc/dex/dex-config.yaml:/etc/dex/config.docker.yaml -p 5556:5556 -p 5558:5558 quay.io/dexidp/dex:latest

  • Dex config in /etc/dex/dex-config.yaml:
issuer: http://[Dex instance public IP]:5556/dex

storage:
  type: memory

web:
  http: 0.0.0.0:5556

telemetry:
  http: 0.0.0.0:5558

grpc:
  addr: 127.0.0.1:5557

logger:
  level: "debug"
  format: "text" # can also be "json"

oauth2:
  responseTypes: [ "code", "token", "id_token" ] # also allowed are "token" and "id_token"

staticClients:
- id: boundary
  name: Boundary
  secret: [client secret]
  redirectUris:
  - [Boundary controller address]/v1/auth-methods/oidc:authenticate:callback

connectors:
- type: google
  id: google
  name: Google public login

enablePasswordDB: true

staticPasswords:
- email: "[email protected]"
  hash: "[bcrypt password hash]"
  username: "jthompson"

Boundary OIDC provider config for Dex:

$ boundary auth-methods read -id amoidc_JZg1tu7M19

Auth Method information:
  Created Time:           Mon, 17 May 2021 02:34:00 EDT
  ID:                     amoidc_JZg1tu7M19
  Is Primary For Scope:   false
  Name:                   Dex
  Type:                   oidc
  Updated Time:           Mon, 17 May 2021 02:36:15 EDT
  Version:                4

  Scope:
    ID:                   global
    Name:                 global
    Type:                 global

  Authorized Actions:
    no-op
    read
    update
    delete
    change-state
    authenticate

  Authorized Actions on Auth Method's Collections:
    accountss:
      create
      list

  Attributes:
    api_url_prefix:       [Boundary controller address]
    callback_url:
    [Boundary controller address]/v1/auth-methods/oidc:authenticate:callback
    client_id:            boundary
    client_secret_hmac:   kqu9d35RUER7qnleiSUmPMaCB9_YYQK_EIsJ1X-X0s0
    issuer:               http://[Dex instance public IP]:5556/dex
    signing_algorithms:   [RS256]
    state:                active-public

Expected behavior

Boundary OIDC should parse the aud claim received from Dex and authenticate the user.

Desktop (please complete the following information):

  • OS: Fedora 34
  • Browser: Firefox
  • Version: 88

Next cap release

Hello there!

We are trying to set up Nomad's login using on-premise ADFS, but we have a problem with OIDC settings.
Since v1.5, nomad has supported OIDC provider from cap repo, but only with enabled discovery which used userinfo endpoint.
We suppose that config with disabled discovery option can help us. I mean changes from PR 2c6463e
My question is when you are going to release them, because I see that the last tag was on April 22.
Thanks.

Add support for static OIDC discovery metadata and JWKs

Is your feature request related to a problem? Please describe.

This library supports OIDC implicit flow (response_type=id_token) which doesn't need direct communication between RP and OP, but it needs direct communication for getting OIDC discovery metadata and JWKs currently.

As a specific use case, I'd like to use https://github.com/hashicorp/vault-plugin-auth-jwt to support authentication by OIDC without direct communication. Currently, CAP does not have the API, so need to implement this using a lot of reflect package as follows, which does not seem like a very good way to do it.

openstandia/vault-plugin-auth-jwt@9eb0e93

Describe the solution you'd like
For OIDC implicit flow without direct communication, I'd like to add support for static OIDC discovery metadata and JWKs.
Currently, provider.go has a factory func. I propose adding feature which creates the provider from static json documents for OIDC discovery metadata and JWKs.

func NewProvider(c *Config) (*Provider, error) {

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.