initial commit
This commit is contained in:
commit
6d0890e280
68 changed files with 5986 additions and 0 deletions
151
pkg/oidc/authorization.go
Normal file
151
pkg/oidc/authorization.go
Normal file
|
@ -0,0 +1,151 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
const (
|
||||
ScopeOpenID = "openid"
|
||||
|
||||
ResponseTypeCode ResponseType = "code"
|
||||
ResponseTypeIDToken ResponseType = "id_token token"
|
||||
ResponseTypeIDTokenOnly ResponseType = "id_token"
|
||||
|
||||
DisplayPage Display = "page"
|
||||
DisplayPopup Display = "popup"
|
||||
DisplayTouch Display = "touch"
|
||||
DisplayWAP Display = "wap"
|
||||
|
||||
PromptNone Prompt = "none"
|
||||
PromptLogin Prompt = "login"
|
||||
PromptConsent Prompt = "consent"
|
||||
PromptSelectAccount Prompt = "select_account"
|
||||
|
||||
GrantTypeCode GrantType = "authorization_code"
|
||||
|
||||
BearerToken = "Bearer"
|
||||
)
|
||||
|
||||
var displayValues = map[string]Display{
|
||||
"page": DisplayPage,
|
||||
"popup": DisplayPopup,
|
||||
"touch": DisplayTouch,
|
||||
"wap": DisplayWAP,
|
||||
}
|
||||
|
||||
//AuthRequest according to:
|
||||
//https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||
//
|
||||
type AuthRequest struct {
|
||||
ID string
|
||||
Scopes Scopes `schema:"scope"`
|
||||
ResponseType ResponseType `schema:"response_type"`
|
||||
ClientID string `schema:"client_id"`
|
||||
RedirectURI string `schema:"redirect_uri"` //TODO: type
|
||||
|
||||
State string `schema:"state"`
|
||||
|
||||
// ResponseMode TODO: ?
|
||||
|
||||
Nonce string `schema:"nonce"`
|
||||
Display Display `schema:"display"`
|
||||
Prompt Prompt `schema:"prompt"`
|
||||
MaxAge uint32 `schema:"max_age"`
|
||||
UILocales Locales `schema:"ui_locales"`
|
||||
IDTokenHint string `schema:"id_token_hint"`
|
||||
LoginHint string `schema:"login_hint"`
|
||||
ACRValues []string `schema:"acr_values"`
|
||||
|
||||
CodeChallenge string `schema:"code_challenge"`
|
||||
CodeChallengeMethod CodeChallengeMethod `schema:"code_challenge_method"`
|
||||
}
|
||||
|
||||
func (a *AuthRequest) GetRedirectURI() string {
|
||||
return a.RedirectURI
|
||||
}
|
||||
func (a *AuthRequest) GetResponseType() ResponseType {
|
||||
return a.ResponseType
|
||||
}
|
||||
func (a *AuthRequest) GetState() string {
|
||||
return a.State
|
||||
}
|
||||
|
||||
type TokenRequest interface {
|
||||
// GrantType GrantType `schema:"grant_type"`
|
||||
GrantType() GrantType
|
||||
}
|
||||
|
||||
type TokenRequestType GrantType
|
||||
|
||||
type AccessTokenRequest struct {
|
||||
Code string `schema:"code"`
|
||||
RedirectURI string `schema:"redirect_uri"`
|
||||
ClientID string `schema:"client_id"`
|
||||
ClientSecret string `schema:"client_secret"`
|
||||
CodeVerifier string `schema:"code_verifier"`
|
||||
}
|
||||
|
||||
func (a *AccessTokenRequest) GrantType() GrantType {
|
||||
return GrantTypeCode
|
||||
}
|
||||
|
||||
type AccessTokenResponse struct {
|
||||
AccessToken string `json:"access_token,omitempty" schema:"access_token,omitempty"`
|
||||
TokenType string `json:"token_type,omitempty" schema:"token_type,omitempty"`
|
||||
RefreshToken string `json:"refresh_token,omitempty" schema:"refresh_token,omitempty"`
|
||||
ExpiresIn uint64 `json:"expires_in,omitempty" schema:"expires_in,omitempty"`
|
||||
IDToken string `json:"id_token,omitempty" schema:"id_token,omitempty"`
|
||||
}
|
||||
|
||||
type TokenExchangeRequest struct {
|
||||
subjectToken string `schema:"subject_token"`
|
||||
subjectTokenType string `schema:"subject_token_type"`
|
||||
actorToken string `schema:"actor_token"`
|
||||
actorTokenType string `schema:"actor_token_type"`
|
||||
resource []string `schema:"resource"`
|
||||
audience []string `schema:"audience"`
|
||||
Scope []string `schema:"scope"`
|
||||
requestedTokenType string `schema:"requested_token_type"`
|
||||
}
|
||||
|
||||
type Scopes []string
|
||||
|
||||
func (s *Scopes) UnmarshalText(text []byte) error {
|
||||
scopes := strings.Split(string(text), " ")
|
||||
*s = Scopes(scopes)
|
||||
return nil
|
||||
}
|
||||
|
||||
type ResponseType string
|
||||
|
||||
type Display string
|
||||
|
||||
func (d *Display) UnmarshalText(text []byte) error {
|
||||
var ok bool
|
||||
display := string(text)
|
||||
*d, ok = displayValues[display]
|
||||
if !ok {
|
||||
return errors.New("")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Prompt string
|
||||
|
||||
type Locales []language.Tag
|
||||
|
||||
func (l *Locales) UnmarshalText(text []byte) error {
|
||||
locales := strings.Split(string(text), " ")
|
||||
for _, locale := range locales {
|
||||
tag, err := language.Parse(locale)
|
||||
if err == nil && !tag.IsRoot() {
|
||||
*l = append(*l, tag)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type GrantType string
|
33
pkg/oidc/code_challenge.go
Normal file
33
pkg/oidc/code_challenge.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
CodeChallengeMethodPlain CodeChallengeMethod = "plain"
|
||||
CodeChallengeMethodS256 CodeChallengeMethod = "S256"
|
||||
)
|
||||
|
||||
type CodeChallengeMethod string
|
||||
|
||||
type CodeChallenge struct {
|
||||
Challenge string
|
||||
Method CodeChallengeMethod
|
||||
}
|
||||
|
||||
func NewSHACodeChallenge(code string) string {
|
||||
return utils.HashString(sha256.New(), code)
|
||||
}
|
||||
|
||||
func VerifyCodeChallenge(c *CodeChallenge, codeVerifier string) bool {
|
||||
if c == nil {
|
||||
return false //TODO: ?
|
||||
}
|
||||
if c.Method == CodeChallengeMethodS256 {
|
||||
codeVerifier = NewSHACodeChallenge(codeVerifier)
|
||||
}
|
||||
return codeVerifier == c.Challenge
|
||||
}
|
24
pkg/oidc/discovery.go
Normal file
24
pkg/oidc/discovery.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package oidc
|
||||
|
||||
const (
|
||||
DiscoveryEndpoint = "/.well-known/openid-configuration"
|
||||
)
|
||||
|
||||
type DiscoveryConfiguration struct {
|
||||
Issuer string `json:"issuer,omitempty"`
|
||||
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
|
||||
TokenEndpoint string `json:"token_endpoint,omitempty"`
|
||||
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
|
||||
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
|
||||
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
|
||||
CheckSessionIframe string `json:"check_session_iframe,omitempty"`
|
||||
JwksURI string `json:"jwks_uri,omitempty"`
|
||||
ScopesSupported []string `json:"scopes_supported,omitempty"`
|
||||
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
|
||||
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
|
||||
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
|
||||
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
|
||||
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
|
||||
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
|
||||
ClaimsSupported []string `json:"claims_supported,omitempty"`
|
||||
}
|
33
pkg/oidc/grants/client_credentials.go
Normal file
33
pkg/oidc/grants/client_credentials.go
Normal file
|
@ -0,0 +1,33 @@
|
|||
package grants
|
||||
|
||||
import "strings"
|
||||
|
||||
type clientCredentialsGrantBasic struct {
|
||||
grantType string `schema:"grant_type"`
|
||||
scope string `schema:"scope"`
|
||||
}
|
||||
|
||||
type clientCredentialsGrant struct {
|
||||
*clientCredentialsGrantBasic
|
||||
clientID string `schema:"client_id"`
|
||||
clientSecret string `schema:"client_secret"`
|
||||
}
|
||||
|
||||
//ClientCredentialsGrantBasic creates an oauth2 `Client Credentials` Grant
|
||||
//sneding client_id and client_secret as basic auth header
|
||||
func ClientCredentialsGrantBasic(scopes ...string) *clientCredentialsGrantBasic {
|
||||
return &clientCredentialsGrantBasic{
|
||||
grantType: "client_credentials",
|
||||
scope: strings.Join(scopes, " "),
|
||||
}
|
||||
}
|
||||
|
||||
//ClientCredentialsGrantValues creates an oauth2 `Client Credentials` Grant
|
||||
//sneding client_id and client_secret as form values
|
||||
func ClientCredentialsGrantValues(clientID, clientSecret string, scopes ...string) *clientCredentialsGrant {
|
||||
return &clientCredentialsGrant{
|
||||
clientCredentialsGrantBasic: ClientCredentialsGrantBasic(scopes...),
|
||||
clientID: clientID,
|
||||
clientSecret: clientSecret,
|
||||
}
|
||||
}
|
75
pkg/oidc/grants/tokenexchange/tokenexchange.go
Normal file
75
pkg/oidc/grants/tokenexchange/tokenexchange.go
Normal file
|
@ -0,0 +1,75 @@
|
|||
package tokenexchange
|
||||
|
||||
const (
|
||||
AccessTokenType = "urn:ietf:params:oauth:token-type:access_token"
|
||||
RefreshTokenType = "urn:ietf:params:oauth:token-type:refresh_token"
|
||||
IDTokenType = "urn:ietf:params:oauth:token-type:id_token"
|
||||
JWTTokenType = "urn:ietf:params:oauth:token-type:jwt"
|
||||
DelegationTokenType = AccessTokenType
|
||||
|
||||
TokenExchangeGrantType = "urn:ietf:params:oauth:grant-type:token-exchange"
|
||||
)
|
||||
|
||||
type TokenExchangeRequest struct {
|
||||
grantType string `schema:"grant_type"`
|
||||
subjectToken string `schema:"subject_token"`
|
||||
subjectTokenType string `schema:"subject_token_type"`
|
||||
actorToken string `schema:"actor_token"`
|
||||
actorTokenType string `schema:"actor_token_type"`
|
||||
resource []string `schema:"resource"`
|
||||
audience []string `schema:"audience"`
|
||||
scope []string `schema:"scope"`
|
||||
requestedTokenType string `schema:"requested_token_type"`
|
||||
}
|
||||
|
||||
func NewTokenExchangeRequest(subjectToken, subjectTokenType string, opts ...TokenExchangeOption) *TokenExchangeRequest {
|
||||
t := &TokenExchangeRequest{
|
||||
grantType: TokenExchangeGrantType,
|
||||
subjectToken: subjectToken,
|
||||
subjectTokenType: subjectTokenType,
|
||||
requestedTokenType: AccessTokenType,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(t)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
type TokenExchangeOption func(*TokenExchangeRequest)
|
||||
|
||||
func WithActorToken(token, tokenType string) func(*TokenExchangeRequest) {
|
||||
return func(req *TokenExchangeRequest) {
|
||||
req.actorToken = token
|
||||
req.actorTokenType = tokenType
|
||||
}
|
||||
}
|
||||
|
||||
func WithAudience(audience []string) func(*TokenExchangeRequest) {
|
||||
return func(req *TokenExchangeRequest) {
|
||||
req.audience = audience
|
||||
}
|
||||
}
|
||||
|
||||
func WithGrantType(grantType string) TokenExchangeOption {
|
||||
return func(req *TokenExchangeRequest) {
|
||||
req.grantType = grantType
|
||||
}
|
||||
}
|
||||
|
||||
func WithRequestedTokenType(tokenType string) func(*TokenExchangeRequest) {
|
||||
return func(req *TokenExchangeRequest) {
|
||||
req.requestedTokenType = tokenType
|
||||
}
|
||||
}
|
||||
|
||||
func WithResource(resource []string) func(*TokenExchangeRequest) {
|
||||
return func(req *TokenExchangeRequest) {
|
||||
req.resource = resource
|
||||
}
|
||||
}
|
||||
|
||||
func WithScope(scope []string) func(*TokenExchangeRequest) {
|
||||
return func(req *TokenExchangeRequest) {
|
||||
req.scope = scope
|
||||
}
|
||||
}
|
22
pkg/oidc/keyset.go
Normal file
22
pkg/oidc/keyset.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
// KeySet is a set of publc JSON Web Keys that can be used to validate the signature
|
||||
// of JSON web tokens. This is expected to be backed by a remote key set through
|
||||
// provider metadata discovery or an in-memory set of keys delivered out-of-band.
|
||||
type KeySet interface {
|
||||
// VerifySignature parses the JSON web token, verifies the signature, and returns
|
||||
// the raw payload. Header and claim fields are validated by other parts of the
|
||||
// package. For example, the KeySet does not need to check values such as signature
|
||||
// algorithm, issuer, and audience since the IDTokenVerifier validates these values
|
||||
// independently.
|
||||
//
|
||||
// If VerifySignature makes HTTP requests to verify the token, it's expected to
|
||||
// use any HTTP client associated with the context through ClientContext.
|
||||
VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error)
|
||||
}
|
196
pkg/oidc/token.go
Normal file
196
pkg/oidc/token.go
Normal file
|
@ -0,0 +1,196 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caos/oidc/pkg/utils"
|
||||
"golang.org/x/oauth2"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
)
|
||||
|
||||
type Tokens struct {
|
||||
*oauth2.Token
|
||||
IDTokenClaims *IDTokenClaims
|
||||
IDToken string
|
||||
}
|
||||
|
||||
type AccessTokenClaims struct {
|
||||
Issuer string
|
||||
Subject string
|
||||
Audiences []string
|
||||
Expiration time.Time
|
||||
IssuedAt time.Time
|
||||
NotBefore time.Time
|
||||
JWTID string
|
||||
AuthorizedParty string
|
||||
Nonce string
|
||||
AuthTime time.Time
|
||||
CodeHash string
|
||||
AuthenticationContextClassReference string
|
||||
AuthenticationMethodsReferences []string
|
||||
SessionID string
|
||||
Scopes []string
|
||||
ClientID string
|
||||
AccessTokenUseNumber int
|
||||
}
|
||||
|
||||
type IDTokenClaims struct {
|
||||
Issuer string
|
||||
Subject string
|
||||
Audiences []string
|
||||
Expiration time.Time
|
||||
NotBefore time.Time
|
||||
IssuedAt time.Time
|
||||
JWTID string
|
||||
UpdatedAt time.Time
|
||||
AuthorizedParty string
|
||||
Nonce string
|
||||
AuthTime time.Time
|
||||
AccessTokenHash string
|
||||
CodeHash string
|
||||
AuthenticationContextClassReference string
|
||||
AuthenticationMethodsReferences []string
|
||||
ClientID string
|
||||
|
||||
Signature jose.SignatureAlgorithm //TODO: ???
|
||||
}
|
||||
|
||||
type jsonToken struct {
|
||||
Issuer string `json:"iss,omitempty"`
|
||||
Subject string `json:"sub,omitempty"`
|
||||
Audiences []string `json:"aud,omitempty"`
|
||||
Expiration int64 `json:"exp,omitempty"`
|
||||
NotBefore int64 `json:"nbf,omitempty"`
|
||||
IssuedAt int64 `json:"iat,omitempty"`
|
||||
JWTID string `json:"jti,omitempty"`
|
||||
UpdatedAt int64 `json:"updated_at,omitempty"`
|
||||
AuthorizedParty string `json:"azp,omitempty"`
|
||||
Nonce string `json:"nonce,omitempty"`
|
||||
AuthTime int64 `json:"auth_time,omitempty"`
|
||||
AccessTokenHash string `json:"at_hash,omitempty"`
|
||||
CodeHash string `json:"c_hash,omitempty"`
|
||||
AuthenticationContextClassReference string `json:"acr,omitempty"`
|
||||
AuthenticationMethodsReferences []string `json:"amr,omitempty"`
|
||||
SessionID string `json:"sid,omitempty"`
|
||||
Actor interface{} `json:"act,omitempty"` //TODO: impl
|
||||
Scopes string `json:"scope,omitempty"`
|
||||
ClientID string `json:"client_id,omitempty"`
|
||||
AuthorizedActor interface{} `json:"may_act,omitempty"` //TODO: impl
|
||||
AccessTokenUseNumber int `json:"at_use_nbr,omitempty"`
|
||||
}
|
||||
|
||||
func (t *AccessTokenClaims) MarshalJSON() ([]byte, error) {
|
||||
j := jsonToken{
|
||||
Issuer: t.Issuer,
|
||||
Subject: t.Subject,
|
||||
Audiences: t.Audiences,
|
||||
Expiration: timeToJSON(t.Expiration),
|
||||
NotBefore: timeToJSON(t.NotBefore),
|
||||
IssuedAt: timeToJSON(t.IssuedAt),
|
||||
JWTID: t.JWTID,
|
||||
AuthorizedParty: t.AuthorizedParty,
|
||||
Nonce: t.Nonce,
|
||||
AuthTime: timeToJSON(t.AuthTime),
|
||||
CodeHash: t.CodeHash,
|
||||
AuthenticationContextClassReference: t.AuthenticationContextClassReference,
|
||||
AuthenticationMethodsReferences: t.AuthenticationMethodsReferences,
|
||||
SessionID: t.SessionID,
|
||||
Scopes: strings.Join(t.Scopes, " "),
|
||||
ClientID: t.ClientID,
|
||||
AccessTokenUseNumber: t.AccessTokenUseNumber,
|
||||
}
|
||||
return json.Marshal(j)
|
||||
}
|
||||
|
||||
func (t *AccessTokenClaims) UnmarshalJSON(b []byte) error {
|
||||
var j jsonToken
|
||||
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.Expiration = time.Unix(j.Expiration, 0).UTC()
|
||||
t.NotBefore = time.Unix(j.NotBefore, 0).UTC()
|
||||
t.IssuedAt = time.Unix(j.IssuedAt, 0).UTC()
|
||||
t.JWTID = j.JWTID
|
||||
t.AuthorizedParty = j.AuthorizedParty
|
||||
t.Nonce = j.Nonce
|
||||
t.AuthTime = time.Unix(j.AuthTime, 0).UTC()
|
||||
t.CodeHash = j.CodeHash
|
||||
t.AuthenticationContextClassReference = j.AuthenticationContextClassReference
|
||||
t.AuthenticationMethodsReferences = j.AuthenticationMethodsReferences
|
||||
t.SessionID = j.SessionID
|
||||
t.Scopes = strings.Split(j.Scopes, " ")
|
||||
t.ClientID = j.ClientID
|
||||
t.AccessTokenUseNumber = j.AccessTokenUseNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *IDTokenClaims) MarshalJSON() ([]byte, error) {
|
||||
j := jsonToken{
|
||||
Issuer: t.Issuer,
|
||||
Subject: t.Subject,
|
||||
Audiences: t.Audiences,
|
||||
Expiration: timeToJSON(t.Expiration),
|
||||
NotBefore: timeToJSON(t.NotBefore),
|
||||
IssuedAt: timeToJSON(t.IssuedAt),
|
||||
JWTID: t.JWTID,
|
||||
UpdatedAt: timeToJSON(t.UpdatedAt),
|
||||
AuthorizedParty: t.AuthorizedParty,
|
||||
Nonce: t.Nonce,
|
||||
AuthTime: timeToJSON(t.AuthTime),
|
||||
AccessTokenHash: t.AccessTokenHash,
|
||||
CodeHash: t.CodeHash,
|
||||
AuthenticationContextClassReference: t.AuthenticationContextClassReference,
|
||||
AuthenticationMethodsReferences: t.AuthenticationMethodsReferences,
|
||||
ClientID: t.ClientID,
|
||||
}
|
||||
return json.Marshal(j)
|
||||
}
|
||||
|
||||
func (t *IDTokenClaims) UnmarshalJSON(b []byte) error {
|
||||
var i jsonToken
|
||||
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.Expiration = time.Unix(i.Expiration, 0).UTC()
|
||||
t.IssuedAt = time.Unix(i.IssuedAt, 0).UTC()
|
||||
t.AuthTime = time.Unix(i.AuthTime, 0).UTC()
|
||||
t.Nonce = i.Nonce
|
||||
t.AuthenticationContextClassReference = i.AuthenticationContextClassReference
|
||||
t.AuthenticationMethodsReferences = i.AuthenticationMethodsReferences
|
||||
t.AuthorizedParty = i.AuthorizedParty
|
||||
t.AccessTokenHash = i.AccessTokenHash
|
||||
t.CodeHash = i.CodeHash
|
||||
return nil
|
||||
}
|
||||
|
||||
func ClaimHash(claim string, sigAlgorithm jose.SignatureAlgorithm) (string, error) {
|
||||
hash, err := utils.GetHashAlgorithm(sigAlgorithm)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return utils.HashString(hash, claim), nil
|
||||
}
|
||||
|
||||
func timeToJSON(t time.Time) int64 {
|
||||
if t.IsZero() {
|
||||
return 0
|
||||
}
|
||||
return t.Unix()
|
||||
}
|
120
pkg/oidc/userinfo.go
Normal file
120
pkg/oidc/userinfo.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
package oidc
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
type Userinfo struct {
|
||||
Subject string
|
||||
Address *UserinfoAddress
|
||||
UserinfoProfile
|
||||
UserinfoEmail
|
||||
UserinfoPhone
|
||||
|
||||
claims map[string]interface{}
|
||||
}
|
||||
|
||||
type UserinfoPhone struct {
|
||||
PhoneNumber string
|
||||
PhoneNumberVerified bool
|
||||
}
|
||||
type UserinfoProfile struct {
|
||||
Name string
|
||||
GivenName string
|
||||
FamilyName string
|
||||
MiddleName string
|
||||
Nickname string
|
||||
Profile string
|
||||
Picture string
|
||||
Website string
|
||||
Gender Gender
|
||||
Birthdate string
|
||||
Zoneinfo string
|
||||
Locale language.Tag
|
||||
UpdatedAt time.Time
|
||||
PreferredUsername string
|
||||
}
|
||||
|
||||
type Gender string
|
||||
|
||||
type UserinfoAddress struct {
|
||||
Formatted string
|
||||
StreetAddress string
|
||||
Locality string
|
||||
Region string
|
||||
PostalCode string
|
||||
Country string
|
||||
}
|
||||
|
||||
type UserinfoEmail struct {
|
||||
Email string
|
||||
EmailVerified bool
|
||||
}
|
||||
|
||||
func marshalUserinfoProfile(i UserinfoProfile, claims map[string]interface{}) {
|
||||
claims["name"] = i.Name
|
||||
claims["given_name"] = i.GivenName
|
||||
claims["family_name"] = i.FamilyName
|
||||
claims["middle_name"] = i.MiddleName
|
||||
claims["nickname"] = i.Nickname
|
||||
claims["profile"] = i.Profile
|
||||
claims["picture"] = i.Picture
|
||||
claims["website"] = i.Website
|
||||
claims["gender"] = i.Gender
|
||||
claims["birthdate"] = i.Birthdate
|
||||
claims["Zoneinfo"] = i.Zoneinfo
|
||||
claims["locale"] = i.Locale.String()
|
||||
claims["updated_at"] = i.UpdatedAt.UTC().Unix()
|
||||
claims["preferred_username"] = i.PreferredUsername
|
||||
}
|
||||
|
||||
func marshalUserinfoEmail(i UserinfoEmail, claims map[string]interface{}) {
|
||||
if i.Email != "" {
|
||||
claims["email"] = i.Email
|
||||
}
|
||||
if i.EmailVerified {
|
||||
claims["email_verified"] = i.EmailVerified
|
||||
}
|
||||
}
|
||||
|
||||
func marshalUserinfoAddress(i *UserinfoAddress, claims map[string]interface{}) {
|
||||
if i == nil {
|
||||
return
|
||||
}
|
||||
address := make(map[string]interface{})
|
||||
if i.Formatted != "" {
|
||||
address["formatted"] = i.Formatted
|
||||
}
|
||||
if i.StreetAddress != "" {
|
||||
address["street_address"] = i.StreetAddress
|
||||
}
|
||||
claims["address"] = address
|
||||
}
|
||||
|
||||
func marshalUserinfoPhone(i UserinfoPhone, claims map[string]interface{}) {
|
||||
claims["phone_number"] = i.PhoneNumber
|
||||
claims["phone_number_verified"] = i.PhoneNumberVerified
|
||||
}
|
||||
|
||||
func (i *Userinfo) MarshalJSON() ([]byte, error) {
|
||||
claims := i.claims
|
||||
if claims == nil {
|
||||
claims = make(map[string]interface{})
|
||||
}
|
||||
claims["sub"] = i.Subject
|
||||
marshalUserinfoAddress(i.Address, claims)
|
||||
marshalUserinfoEmail(i.UserinfoEmail, claims)
|
||||
marshalUserinfoPhone(i.UserinfoPhone, claims)
|
||||
marshalUserinfoProfile(i.UserinfoProfile, claims)
|
||||
return json.Marshal(claims)
|
||||
}
|
||||
|
||||
func (i *Userinfo) UnmmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, i); err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, i.claims)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue