fix: handle single aud
string claim, extract en/decoder interface, comments (#51)
* en/decoding abstraction * some comments * fix token validation and error messages * fix: audience mapping (single aud string) * fix tests with VerifyIdToken * reformat imports * go mod tidy * Update pkg/oidc/authorization.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update pkg/oidc/authorization.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * Update pkg/op/authrequest_test.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * fix capitalization Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
parent
822ffb581f
commit
abd3b6f521
24 changed files with 381 additions and 139 deletions
|
@ -8,10 +8,40 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
//ScopeOpenID defines the scope `openid`
|
||||
//OpenID Connect requests MUST contain the `openid` scope value
|
||||
ScopeOpenID = "openid"
|
||||
|
||||
ResponseTypeCode ResponseType = "code"
|
||||
ResponseTypeIDToken ResponseType = "id_token token"
|
||||
//ScopeProfile defines the scope `profile`
|
||||
//This (optional) scope value requests access to the End-User's default profile Claims,
|
||||
//which are: name, family_name, given_name, middle_name, nickname, preferred_username,
|
||||
//profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at.
|
||||
ScopeProfile = "profile"
|
||||
|
||||
//ScopeEmail defines the scope `email`
|
||||
//This (optional) scope value requests access to the email and email_verified Claims.
|
||||
ScopeEmail = "email"
|
||||
|
||||
//ScopeAddress defines the scope `address`
|
||||
//This (optional) scope value requests access to the address Claim.
|
||||
ScopeAddress = "address"
|
||||
|
||||
//ScopePhone defines the scope `phone`
|
||||
//This (optional) scope value requests access to the phone_number and phone_number_verified Claims.
|
||||
ScopePhone = "phone"
|
||||
|
||||
//ScopeOfflineAccess defines the scope `offline_access`
|
||||
//This (optional) scope value requests that an OAuth 2.0 Refresh Token be issued that can be used to obtain an Access Token
|
||||
//that grants access to the End-User's UserInfo Endpoint even when the End-User is not present (not logged in).
|
||||
ScopeOfflineAccess = "offline_access"
|
||||
|
||||
//ResponseTypeCode for the Authorization Code Flow returning a code from the Authorization Server
|
||||
ResponseTypeCode ResponseType = "code"
|
||||
|
||||
//ResponseTypeIDToken for the Implicit Flow returning id and access tokens directly from the Authorization Server
|
||||
ResponseTypeIDToken ResponseType = "id_token token"
|
||||
|
||||
//ResponseTypeIDTokenOnly for the Implicit Flow returning only id token directly from the Authorization Server
|
||||
ResponseTypeIDTokenOnly ResponseType = "id_token"
|
||||
|
||||
DisplayPage Display = "page"
|
||||
|
@ -19,13 +49,23 @@ const (
|
|||
DisplayTouch Display = "touch"
|
||||
DisplayWAP Display = "wap"
|
||||
|
||||
PromptNone Prompt = "none"
|
||||
PromptLogin Prompt = "login"
|
||||
PromptConsent Prompt = "consent"
|
||||
//PromptNone (`none`) disallows the Authorization Server to display any authentication or consent user interface pages.
|
||||
//An error (login_required, interaction_required, ...) will be returned if the user is not already authenticated or consent is needed
|
||||
PromptNone Prompt = "none"
|
||||
|
||||
//PromptLogin (`login`) directs the Authorization Server to prompt the End-User for reauthentication.
|
||||
PromptLogin Prompt = "login"
|
||||
|
||||
//PromptConsent (`consent`) directs the Authorization Server to prompt the End-User for consent (of sharing information).
|
||||
PromptConsent Prompt = "consent"
|
||||
|
||||
//PromptSelectAccount (`select_account `) directs the Authorization Server to prompt the End-User to select a user account (to enable multi user / session switching)
|
||||
PromptSelectAccount Prompt = "select_account"
|
||||
|
||||
//GrantTypeCode defines the grant_type `authorization_code` used for the Token Request in the Authorization Code Flow
|
||||
GrantTypeCode GrantType = "authorization_code"
|
||||
|
||||
//BearerToken defines the token_type `Bearer`, which is returned in a successful token response
|
||||
BearerToken = "Bearer"
|
||||
)
|
||||
|
||||
|
@ -38,7 +78,6 @@ var displayValues = map[string]Display{
|
|||
|
||||
//AuthRequest according to:
|
||||
//https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||
//
|
||||
type AuthRequest struct {
|
||||
ID string
|
||||
Scopes Scopes `schema:"scope"`
|
||||
|
@ -63,12 +102,17 @@ type AuthRequest struct {
|
|||
CodeChallengeMethod CodeChallengeMethod `schema:"code_challenge_method"`
|
||||
}
|
||||
|
||||
//GetRedirectURI returns the redirect_uri value for the ErrAuthRequest interface
|
||||
func (a *AuthRequest) GetRedirectURI() string {
|
||||
return a.RedirectURI
|
||||
}
|
||||
|
||||
//GetResponseType returns the response_type value for the ErrAuthRequest interface
|
||||
func (a *AuthRequest) GetResponseType() ResponseType {
|
||||
return a.ResponseType
|
||||
}
|
||||
|
||||
//GetState returns the optional state value for the ErrAuthRequest interface
|
||||
func (a *AuthRequest) GetState() string {
|
||||
return a.State
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/text/language"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
||||
type Tokens struct {
|
||||
|
@ -61,7 +62,7 @@ type IDTokenClaims struct {
|
|||
type jsonToken struct {
|
||||
Issuer string `json:"iss,omitempty"`
|
||||
Subject string `json:"sub,omitempty"`
|
||||
Audiences []string `json:"aud,omitempty"`
|
||||
Audiences interface{} `json:"aud,omitempty"`
|
||||
Expiration int64 `json:"exp,omitempty"`
|
||||
NotBefore int64 `json:"nbf,omitempty"`
|
||||
IssuedAt int64 `json:"iat,omitempty"`
|
||||
|
@ -110,13 +111,9 @@ func (t *AccessTokenClaims) UnmarshalJSON(b []byte) error {
|
|||
if err := json.Unmarshal(b, &j); err != nil {
|
||||
return err
|
||||
}
|
||||
audience := j.Audiences
|
||||
if len(audience) == 1 {
|
||||
audience = strings.Split(audience[0], " ")
|
||||
}
|
||||
t.Issuer = j.Issuer
|
||||
t.Subject = j.Subject
|
||||
t.Audiences = audience
|
||||
t.Audiences = audienceFromJSON(j.Audiences)
|
||||
t.Expiration = time.Unix(j.Expiration, 0).UTC()
|
||||
t.NotBefore = time.Unix(j.NotBefore, 0).UTC()
|
||||
t.IssuedAt = time.Unix(j.IssuedAt, 0).UTC()
|
||||
|
@ -161,13 +158,9 @@ func (t *IDTokenClaims) UnmarshalJSON(b []byte) error {
|
|||
if err := json.Unmarshal(b, &i); err != nil {
|
||||
return err
|
||||
}
|
||||
audience := i.Audiences
|
||||
if len(audience) == 1 {
|
||||
audience = strings.Split(audience[0], " ")
|
||||
}
|
||||
t.Issuer = i.Issuer
|
||||
t.Subject = i.Subject
|
||||
t.Audiences = audience
|
||||
t.Audiences = audienceFromJSON(i.Audiences)
|
||||
t.Expiration = time.Unix(i.Expiration, 0).UTC()
|
||||
t.IssuedAt = time.Unix(i.IssuedAt, 0).UTC()
|
||||
t.AuthTime = time.Unix(i.AuthTime, 0).UTC()
|
||||
|
@ -247,3 +240,13 @@ func timeToJSON(t time.Time) int64 {
|
|||
}
|
||||
return t.Unix()
|
||||
}
|
||||
|
||||
func audienceFromJSON(audience interface{}) []string {
|
||||
switch aud := audience.(type) {
|
||||
case []string:
|
||||
return aud
|
||||
case string:
|
||||
return []string{aud}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue