BREAKING CHANGE: - The various verifier types are merged into a oidc.Verifir. - oidc.Verfier became a struct with exported fields * use type aliases for oidc.Verifier this binds the correct contstructor to each verifier usecase. * fix: handle the zero cases for oidc.Time * add unit tests to oidc verifier * fix: correct returned field for JWTTokenRequest JWTTokenRequest.GetIssuedAt() was returning the ExpiresAt field. This change corrects that by returning IssuedAt instead.
227 lines
4.2 KiB
Go
227 lines
4.2 KiB
Go
package oidc
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gorilla/schema"
|
|
"golang.org/x/text/language"
|
|
"gopkg.in/square/go-jose.v2"
|
|
)
|
|
|
|
type Audience []string
|
|
|
|
func (a *Audience) UnmarshalJSON(text []byte) error {
|
|
var i interface{}
|
|
err := json.Unmarshal(text, &i)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
switch aud := i.(type) {
|
|
case []interface{}:
|
|
*a = make([]string, len(aud))
|
|
for i, audience := range aud {
|
|
(*a)[i] = audience.(string)
|
|
}
|
|
case string:
|
|
*a = []string{aud}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type Display string
|
|
|
|
func (d *Display) UnmarshalText(text []byte) error {
|
|
display := Display(text)
|
|
switch display {
|
|
case DisplayPage, DisplayPopup, DisplayTouch, DisplayWAP:
|
|
*d = display
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type Gender string
|
|
|
|
type Locale struct {
|
|
tag language.Tag
|
|
}
|
|
|
|
func NewLocale(tag language.Tag) *Locale {
|
|
return &Locale{tag: tag}
|
|
}
|
|
|
|
func (l *Locale) Tag() language.Tag {
|
|
if l == nil {
|
|
return language.Und
|
|
}
|
|
|
|
return l.tag
|
|
}
|
|
|
|
func (l *Locale) String() string {
|
|
return l.Tag().String()
|
|
}
|
|
|
|
func (l *Locale) MarshalJSON() ([]byte, error) {
|
|
tag := l.Tag()
|
|
if tag.IsRoot() {
|
|
return []byte("null"), nil
|
|
}
|
|
|
|
return json.Marshal(tag)
|
|
}
|
|
|
|
func (l *Locale) UnmarshalJSON(data []byte) error {
|
|
return json.Unmarshal(data, &l.tag)
|
|
}
|
|
|
|
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 MaxAge *uint
|
|
|
|
func NewMaxAge(i uint) MaxAge {
|
|
return &i
|
|
}
|
|
|
|
type SpaceDelimitedArray []string
|
|
|
|
type Prompt SpaceDelimitedArray
|
|
|
|
type ResponseType string
|
|
|
|
type ResponseMode string
|
|
|
|
func (s SpaceDelimitedArray) Encode() string {
|
|
return strings.Join(s, " ")
|
|
}
|
|
|
|
func (s *SpaceDelimitedArray) UnmarshalText(text []byte) error {
|
|
*s = strings.Split(string(text), " ")
|
|
return nil
|
|
}
|
|
|
|
func (s SpaceDelimitedArray) MarshalText() ([]byte, error) {
|
|
return []byte(s.Encode()), nil
|
|
}
|
|
|
|
func (s SpaceDelimitedArray) MarshalJSON() ([]byte, error) {
|
|
return json.Marshal((s).Encode())
|
|
}
|
|
|
|
func (s *SpaceDelimitedArray) UnmarshalJSON(data []byte) error {
|
|
var str string
|
|
if err := json.Unmarshal(data, &str); err != nil {
|
|
return err
|
|
}
|
|
*s = strings.Split(str, " ")
|
|
return nil
|
|
}
|
|
|
|
func (s *SpaceDelimitedArray) Scan(src interface{}) error {
|
|
if src == nil {
|
|
*s = nil
|
|
return nil
|
|
}
|
|
switch v := src.(type) {
|
|
case string:
|
|
if len(v) == 0 {
|
|
*s = SpaceDelimitedArray{}
|
|
return nil
|
|
}
|
|
*s = strings.Split(v, " ")
|
|
case []byte:
|
|
if len(v) == 0 {
|
|
*s = SpaceDelimitedArray{}
|
|
return nil
|
|
}
|
|
*s = strings.Split(string(v), " ")
|
|
default:
|
|
return fmt.Errorf("cannot convert %T to SpaceDelimitedArray", src)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s SpaceDelimitedArray) Value() (driver.Value, error) {
|
|
return strings.Join(s, " "), nil
|
|
}
|
|
|
|
// NewEncoder returns a schema Encoder with
|
|
// a registered encoder for SpaceDelimitedArray.
|
|
func NewEncoder() *schema.Encoder {
|
|
e := schema.NewEncoder()
|
|
e.RegisterEncoder(SpaceDelimitedArray{}, func(value reflect.Value) string {
|
|
return value.Interface().(SpaceDelimitedArray).Encode()
|
|
})
|
|
return e
|
|
}
|
|
|
|
type Time int64
|
|
|
|
func (ts Time) AsTime() time.Time {
|
|
if ts == 0 {
|
|
return time.Time{}
|
|
}
|
|
return time.Unix(int64(ts), 0)
|
|
}
|
|
|
|
func FromTime(tt time.Time) Time {
|
|
if tt.IsZero() {
|
|
return 0
|
|
}
|
|
return Time(tt.Unix())
|
|
}
|
|
|
|
func NowTime() Time {
|
|
return FromTime(time.Now())
|
|
}
|
|
|
|
func (ts *Time) UnmarshalJSON(data []byte) error {
|
|
var v any
|
|
if err := json.Unmarshal(data, &v); err != nil {
|
|
return fmt.Errorf("oidc.Time: %w", err)
|
|
}
|
|
switch x := v.(type) {
|
|
case float64:
|
|
*ts = Time(x)
|
|
case string:
|
|
// Compatibility with Auth0:
|
|
// https://github.com/zitadel/oidc/issues/292
|
|
tt, err := time.Parse(time.RFC3339, x)
|
|
if err != nil {
|
|
return fmt.Errorf("oidc.Time: %w", err)
|
|
}
|
|
*ts = FromTime(tt)
|
|
case nil:
|
|
*ts = 0
|
|
default:
|
|
return fmt.Errorf("oidc.Time: unable to parse type %T with value %v", x, x)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type RequestObject struct {
|
|
Issuer string `json:"iss"`
|
|
Audience Audience `json:"aud"`
|
|
AuthRequest
|
|
}
|
|
|
|
func (r *RequestObject) GetIssuer() string {
|
|
return r.Issuer
|
|
}
|
|
|
|
func (*RequestObject) SetSignatureAlgorithm(algorithm jose.SignatureAlgorithm) {}
|