introspect
This commit is contained in:
parent
134999bc33
commit
138da8a208
13 changed files with 305 additions and 98 deletions
|
@ -3,6 +3,8 @@ package op
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
@ -24,17 +26,22 @@ func CreateDiscoveryConfig(c Configuration, s Signer) *oidc.DiscoveryConfigurati
|
|||
TokenEndpoint: c.TokenEndpoint().Absolute(c.Issuer()),
|
||||
IntrospectionEndpoint: c.IntrospectionEndpoint().Absolute(c.Issuer()),
|
||||
UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()),
|
||||
EndSessionEndpoint: c.EndSessionEndpoint().Absolute(c.Issuer()),
|
||||
//RevocationEndpoint: c.RevocationEndpoint().Absolute(c.Issuer()),
|
||||
EndSessionEndpoint: c.EndSessionEndpoint().Absolute(c.Issuer()),
|
||||
// CheckSessionIframe: c.TokenEndpoint().Absolute(c.Issuer())(c.CheckSessionIframe),
|
||||
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
|
||||
ScopesSupported: Scopes(c),
|
||||
ResponseTypesSupported: ResponseTypes(c),
|
||||
GrantTypesSupported: GrantTypes(c),
|
||||
ClaimsSupported: SupportedClaims(c),
|
||||
IDTokenSigningAlgValuesSupported: SigAlgorithms(s),
|
||||
SubjectTypesSupported: SubjectTypes(c),
|
||||
TokenEndpointAuthMethodsSupported: AuthMethods(c),
|
||||
CodeChallengeMethodsSupported: CodeChallengeMethods(c),
|
||||
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
|
||||
ScopesSupported: Scopes(c),
|
||||
ResponseTypesSupported: ResponseTypes(c),
|
||||
//ResponseModesSupported:
|
||||
GrantTypesSupported: GrantTypes(c),
|
||||
//ACRValuesSupported: ACRValues(c),
|
||||
SubjectTypesSupported: SubjectTypes(c),
|
||||
IDTokenSigningAlgValuesSupported: SigAlgorithms(s),
|
||||
TokenEndpointAuthMethodsSupported: AuthMethodsTokenEndpoint(c),
|
||||
IntrospectionEndpointAuthMethodsSupported: AuthMethodsIntrospectionEndpoint(c),
|
||||
ClaimsSupported: SupportedClaims(c),
|
||||
CodeChallengeMethodsSupported: CodeChallengeMethods(c),
|
||||
UILocalesSupported: UILocales(c),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,15 +65,16 @@ func ResponseTypes(c Configuration) []string {
|
|||
} //TODO: ok for now, check later if dynamic needed
|
||||
}
|
||||
|
||||
func GrantTypes(c Configuration) []string {
|
||||
grantTypes := []string{
|
||||
string(oidc.GrantTypeCode),
|
||||
func GrantTypes(c Configuration) []oidc.GrantType {
|
||||
grantTypes := []oidc.GrantType{
|
||||
oidc.GrantTypeCode,
|
||||
oidc.GrantTypeImplicit,
|
||||
}
|
||||
if c.GrantTypeTokenExchangeSupported() {
|
||||
grantTypes = append(grantTypes, string(oidc.GrantTypeTokenExchange))
|
||||
grantTypes = append(grantTypes, oidc.GrantTypeTokenExchange)
|
||||
}
|
||||
if c.GrantTypeJWTAuthorizationSupported() {
|
||||
grantTypes = append(grantTypes, string(oidc.GrantTypeBearer))
|
||||
grantTypes = append(grantTypes, oidc.GrantTypeBearer)
|
||||
}
|
||||
return grantTypes
|
||||
}
|
||||
|
@ -108,7 +116,7 @@ func SubjectTypes(c Configuration) []string {
|
|||
return []string{"public"} //TODO: config
|
||||
}
|
||||
|
||||
func AuthMethods(c Configuration) []oidc.AuthMethod {
|
||||
func AuthMethodsTokenEndpoint(c Configuration) []oidc.AuthMethod {
|
||||
authMethods := []oidc.AuthMethod{
|
||||
oidc.AuthMethodNone,
|
||||
oidc.AuthMethodBasic,
|
||||
|
@ -122,6 +130,16 @@ func AuthMethods(c Configuration) []oidc.AuthMethod {
|
|||
return authMethods
|
||||
}
|
||||
|
||||
func AuthMethodsIntrospectionEndpoint(c Configuration) []oidc.AuthMethod {
|
||||
authMethods := []oidc.AuthMethod{
|
||||
oidc.AuthMethodBasic,
|
||||
}
|
||||
if c.AuthMethodPrivateKeyJWTSupported() {
|
||||
authMethods = append(authMethods, oidc.AuthMethodPrivateKeyJWT)
|
||||
}
|
||||
return authMethods
|
||||
}
|
||||
|
||||
func CodeChallengeMethods(c Configuration) []oidc.CodeChallengeMethod {
|
||||
codeMethods := make([]oidc.CodeChallengeMethod, 0, 1)
|
||||
if c.CodeMethodS256Supported() {
|
||||
|
@ -129,3 +147,10 @@ func CodeChallengeMethods(c Configuration) []oidc.CodeChallengeMethod {
|
|||
}
|
||||
return codeMethods
|
||||
}
|
||||
|
||||
func UILocales(c Configuration) []language.Tag {
|
||||
return []language.Tag{
|
||||
language.English,
|
||||
language.German,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ type OPStorage interface {
|
|||
AuthorizeClientIDSecret(ctx context.Context, clientID, clientSecret string) error
|
||||
SetUserinfoFromScopes(ctx context.Context, userinfo oidc.UserInfoSetter, userID, clientID string, scopes []string) error
|
||||
SetUserinfoFromToken(ctx context.Context, userinfo oidc.UserInfoSetter, tokenID, subject, origin string) error
|
||||
SetIntrospectionFromToken(ctx context.Context, userinfo oidc.IntrospectionResponse, tokenID, subject, callerTokenID, callerSubject string) error
|
||||
SetIntrospectionFromToken(ctx context.Context, userinfo oidc.IntrospectionResponse, tokenID, subject, clientID string) error
|
||||
GetPrivateClaimsFromScopes(ctx context.Context, userID, clientID string, scopes []string) (map[string]interface{}, error)
|
||||
GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (*jose.JSONWebKey, error)
|
||||
ValidateJWTProfileScopes(ctx context.Context, userID string, scope oidc.Scopes) (oidc.Scopes, error)
|
||||
|
|
|
@ -3,7 +3,6 @@ package op
|
|||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
|
@ -16,6 +15,11 @@ type Introspector interface {
|
|||
AccessTokenVerifier() AccessTokenVerifier
|
||||
}
|
||||
|
||||
type IntrospectorJWTProfile interface {
|
||||
Introspector
|
||||
JWTProfileVerifier() JWTProfileVerifier
|
||||
}
|
||||
|
||||
func introspectionHandler(introspector Introspector) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
Introspect(w, r, introspector)
|
||||
|
@ -23,24 +27,18 @@ func introspectionHandler(introspector Introspector) func(http.ResponseWriter, *
|
|||
}
|
||||
|
||||
func Introspect(w http.ResponseWriter, r *http.Request, introspector Introspector) {
|
||||
callerToken := r.Header.Get("authorization")
|
||||
response := oidc.NewIntrospectionResponse()
|
||||
callerToken, callerSubject, ok := getTokenIDAndSubject(r.Context(), introspector, strings.TrimPrefix(callerToken, oidc.PrefixBearer))
|
||||
if !ok {
|
||||
utils.MarshalJSON(w, response)
|
||||
return
|
||||
}
|
||||
introspectionToken, err := ParseTokenInrospectionRequest(r, introspector.Decoder())
|
||||
token, clientID, err := ParseTokenIntrospectionRequest(r, introspector)
|
||||
if err != nil {
|
||||
utils.MarshalJSON(w, response)
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
tokenID, subject, ok := getTokenIDAndSubject(r.Context(), introspector, introspectionToken)
|
||||
tokenID, subject, ok := getTokenIDAndSubject(r.Context(), introspector, token)
|
||||
if !ok {
|
||||
utils.MarshalJSON(w, response)
|
||||
return
|
||||
}
|
||||
err = introspector.Storage().SetIntrospectionFromToken(r.Context(), response, tokenID, subject, callerToken, callerSubject)
|
||||
err = introspector.Storage().SetIntrospectionFromToken(r.Context(), response, tokenID, subject, clientID)
|
||||
if err != nil {
|
||||
utils.MarshalJSON(w, response)
|
||||
return
|
||||
|
@ -49,15 +47,31 @@ func Introspect(w http.ResponseWriter, r *http.Request, introspector Introspecto
|
|||
utils.MarshalJSON(w, response)
|
||||
}
|
||||
|
||||
func ParseTokenInrospectionRequest(r *http.Request, decoder utils.Decoder) (string, error) {
|
||||
err := r.ParseForm()
|
||||
func ParseTokenIntrospectionRequest(r *http.Request, introspector Introspector) (token, clientID string, err error) {
|
||||
err = r.ParseForm()
|
||||
if err != nil {
|
||||
return "", errors.New("unable to parse request")
|
||||
return "", "", errors.New("unable to parse request")
|
||||
}
|
||||
req := new(oidc.IntrospectionRequest)
|
||||
err = decoder.Decode(req, r.Form)
|
||||
req := new(struct {
|
||||
oidc.IntrospectionRequest
|
||||
oidc.ClientAssertionParams
|
||||
})
|
||||
err = introspector.Decoder().Decode(req, r.Form)
|
||||
if err != nil {
|
||||
return "", errors.New("unable to parse request")
|
||||
return "", "", errors.New("unable to parse request")
|
||||
}
|
||||
return req.Token, nil
|
||||
if introspectorJWTProfile, ok := introspector.(IntrospectorJWTProfile); ok && req.ClientAssertion != "" {
|
||||
profile, err := VerifyJWTAssertion(r.Context(), req.ClientAssertion, introspectorJWTProfile.JWTProfileVerifier())
|
||||
if err == nil {
|
||||
return req.Token, profile.Issuer, nil
|
||||
}
|
||||
}
|
||||
clientID, clientSecret, ok := r.BasicAuth()
|
||||
if ok {
|
||||
if err := introspector.Storage().AuthorizeClientIDSecret(r.Context(), clientID, clientSecret); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return req.Token, clientID, nil
|
||||
}
|
||||
return "", "", errors.New("invalid authorization")
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ func userinfoHandler(userinfoProvider UserinfoProvider) func(http.ResponseWriter
|
|||
}
|
||||
|
||||
func Userinfo(w http.ResponseWriter, r *http.Request, userinfoProvider UserinfoProvider) {
|
||||
accessToken, err := getAccessToken(r, userinfoProvider.Decoder())
|
||||
accessToken, err := ParseUserinfoRequest(r, userinfoProvider.Decoder())
|
||||
if err != nil {
|
||||
http.Error(w, "access token missing", http.StatusUnauthorized)
|
||||
return
|
||||
|
@ -43,16 +43,12 @@ func Userinfo(w http.ResponseWriter, r *http.Request, userinfoProvider UserinfoP
|
|||
utils.MarshalJSON(w, info)
|
||||
}
|
||||
|
||||
func getAccessToken(r *http.Request, decoder utils.Decoder) (string, error) {
|
||||
authHeader := r.Header.Get("authorization")
|
||||
if authHeader != "" {
|
||||
parts := strings.Split(authHeader, "Bearer ")
|
||||
if len(parts) != 2 {
|
||||
return "", errors.New("invalid auth header")
|
||||
}
|
||||
return parts[1], nil
|
||||
func ParseUserinfoRequest(r *http.Request, decoder utils.Decoder) (string, error) {
|
||||
accessToken, err := getAccessToken(r)
|
||||
if err == nil {
|
||||
return accessToken, nil
|
||||
}
|
||||
err := r.ParseForm()
|
||||
err = r.ParseForm()
|
||||
if err != nil {
|
||||
return "", errors.New("unable to parse request")
|
||||
}
|
||||
|
@ -64,6 +60,18 @@ func getAccessToken(r *http.Request, decoder utils.Decoder) (string, error) {
|
|||
return req.AccessToken, nil
|
||||
}
|
||||
|
||||
func getAccessToken(r *http.Request) (string, error) {
|
||||
authHeader := r.Header.Get("authorization")
|
||||
if authHeader == "" {
|
||||
return "", errors.New("no auth header")
|
||||
}
|
||||
parts := strings.Split(authHeader, "Bearer ")
|
||||
if len(parts) != 2 {
|
||||
return "", errors.New("invalid auth header")
|
||||
}
|
||||
return parts[1], nil
|
||||
}
|
||||
|
||||
func getTokenIDAndSubject(ctx context.Context, userinfoProvider UserinfoProvider, accessToken string) (string, string, bool) {
|
||||
tokenIDSubject, err := userinfoProvider.Crypto().Decrypt(accessToken)
|
||||
if err == nil {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue