FEATURES (and FIXES): - support OAuth 2.0 Token Revocation [RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009) - handle request object using `request` parameter [OIDC Core 1.0 Request Object](https://openid.net/specs/openid-connect-core-1_0.html#RequestObject) - handle response mode - added some information to the discovery endpoint: - revocation_endpoint (added with token revocation) - revocation_endpoint_auth_methods_supported (added with token revocation) - revocation_endpoint_auth_signing_alg_values_supported (added with token revocation) - token_endpoint_auth_signing_alg_values_supported (was missing) - introspection_endpoint_auth_signing_alg_values_supported (was missing) - request_object_signing_alg_values_supported (added with request object) - request_parameter_supported (added with request object) - fixed `removeUserinfoScopes ` now returns the scopes without "userinfo" scopes (profile, email, phone, addedd) [source diff](https://github.com/caos/oidc/pull/130/files#diff-fad50c8c0f065d4dbc49d6c6a38f09c992c8f5d651a479ba00e31b500543559eL170-R171) - improved error handling (pkg/oidc/error.go) and fixed some wrong OAuth errors (e.g. `invalid_grant` instead of `invalid_request`) - improved MarshalJSON and added MarshalJSONWithStatus - removed deprecated PEM decryption from `BytesToPrivateKey` [source diff](https://github.com/caos/oidc/pull/130/files#diff-fe246e428e399ccff599627c71764de51387b60b4df84c67de3febd0954e859bL11-L19) - NewAccessTokenVerifier now uses correct (internal) `accessTokenVerifier` [source diff](https://github.com/caos/oidc/pull/130/files#diff-3a01c7500ead8f35448456ef231c7c22f8d291710936cac91de5edeef52ffc72L52-R52) BREAKING CHANGE: - move functions from `utils` package into separate packages - added various methods to the (OP) `Configuration` interface [source diff](https://github.com/caos/oidc/pull/130/files#diff-2538e0dfc772fdc37f057aecd6fcc2943f516c24e8be794cce0e368a26d20a82R19-R32) - added revocationEndpoint to `WithCustomEndpoints ` [source diff](https://github.com/caos/oidc/pull/130/files#diff-19ae13a743eb7cebbb96492798b1bec556673eb6236b1387e38d722900bae1c3L355-R391) - remove unnecessary context parameter from JWTProfileExchange [source diff](https://github.com/caos/oidc/pull/130/files#diff-4ed8f6affa4a9631fa8a034b3d5752fbb6a819107141aae00029014e950f7b4cL14)
94 lines
2.7 KiB
Go
94 lines
2.7 KiB
Go
package client
|
|
|
|
import (
|
|
"net/http"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gorilla/schema"
|
|
"golang.org/x/oauth2"
|
|
"gopkg.in/square/go-jose.v2"
|
|
|
|
"github.com/caos/oidc/pkg/crypto"
|
|
httphelper "github.com/caos/oidc/pkg/http"
|
|
"github.com/caos/oidc/pkg/oidc"
|
|
)
|
|
|
|
var (
|
|
Encoder = func() httphelper.Encoder {
|
|
e := schema.NewEncoder()
|
|
e.RegisterEncoder(oidc.SpaceDelimitedArray{}, func(value reflect.Value) string {
|
|
return value.Interface().(oidc.SpaceDelimitedArray).Encode()
|
|
})
|
|
return e
|
|
}()
|
|
)
|
|
|
|
//Discover calls the discovery endpoint of the provided issuer and returns its configuration
|
|
func Discover(issuer string, httpClient *http.Client) (*oidc.DiscoveryConfiguration, error) {
|
|
wellKnown := strings.TrimSuffix(issuer, "/") + oidc.DiscoveryEndpoint
|
|
req, err := http.NewRequest("GET", wellKnown, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
discoveryConfig := new(oidc.DiscoveryConfiguration)
|
|
err = httphelper.HttpRequest(httpClient, req, &discoveryConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if discoveryConfig.Issuer != issuer {
|
|
return nil, oidc.ErrIssuerInvalid
|
|
}
|
|
return discoveryConfig, nil
|
|
}
|
|
|
|
type tokenEndpointCaller interface {
|
|
TokenEndpoint() string
|
|
HttpClient() *http.Client
|
|
}
|
|
|
|
func CallTokenEndpoint(request interface{}, caller tokenEndpointCaller) (newToken *oauth2.Token, err error) {
|
|
return callTokenEndpoint(request, nil, caller)
|
|
}
|
|
|
|
func callTokenEndpoint(request interface{}, authFn interface{}, caller tokenEndpointCaller) (newToken *oauth2.Token, err error) {
|
|
req, err := httphelper.FormRequest(caller.TokenEndpoint(), request, Encoder, authFn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tokenRes := new(oidc.AccessTokenResponse)
|
|
if err := httphelper.HttpRequest(caller.HttpClient(), req, &tokenRes); err != nil {
|
|
return nil, err
|
|
}
|
|
return &oauth2.Token{
|
|
AccessToken: tokenRes.AccessToken,
|
|
TokenType: tokenRes.TokenType,
|
|
RefreshToken: tokenRes.RefreshToken,
|
|
Expiry: time.Now().UTC().Add(time.Duration(tokenRes.ExpiresIn) * time.Second),
|
|
}, nil
|
|
}
|
|
|
|
func NewSignerFromPrivateKeyByte(key []byte, keyID string) (jose.Signer, error) {
|
|
privateKey, err := crypto.BytesToPrivateKey(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
signingKey := jose.SigningKey{
|
|
Algorithm: jose.RS256,
|
|
Key: &jose.JSONWebKey{Key: privateKey, KeyID: keyID},
|
|
}
|
|
return jose.NewSigner(signingKey, &jose.SignerOptions{})
|
|
}
|
|
|
|
func SignedJWTProfileAssertion(clientID string, audience []string, expiration time.Duration, signer jose.Signer) (string, error) {
|
|
iat := time.Now()
|
|
exp := iat.Add(expiration)
|
|
return crypto.Sign(&oidc.JWTTokenRequest{
|
|
Issuer: clientID,
|
|
Subject: clientID,
|
|
Audience: audience,
|
|
ExpiresAt: oidc.Time(exp),
|
|
IssuedAt: oidc.Time(iat),
|
|
}, signer)
|
|
}
|