(discovery) config and storage
This commit is contained in:
parent
ce6f3182a2
commit
ecea7e3730
9 changed files with 132 additions and 38 deletions
|
@ -12,18 +12,20 @@ import (
|
||||||
"github.com/caos/oidc/pkg/op"
|
"github.com/caos/oidc/pkg/op"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Storage struct {
|
type AuthStorage struct {
|
||||||
key *rsa.PrivateKey
|
key *rsa.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStorage() op.Storage {
|
type OPStorage struct{}
|
||||||
|
|
||||||
|
func NewAuthStorage() op.AuthStorage {
|
||||||
reader := rand.Reader
|
reader := rand.Reader
|
||||||
bitSize := 2048
|
bitSize := 2048
|
||||||
key, err := rsa.GenerateKey(reader, bitSize)
|
key, err := rsa.GenerateKey(reader, bitSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return &Storage{
|
return &AuthStorage{
|
||||||
key: key,
|
key: key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,10 +82,10 @@ func (a *AuthRequest) GetSubject() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) CreateAuthRequest(authReq *oidc.AuthRequest) (op.AuthRequest, error) {
|
func (s *AuthStorage) CreateAuthRequest(authReq *oidc.AuthRequest) (op.AuthRequest, error) {
|
||||||
return &AuthRequest{ID: "id"}, nil
|
return &AuthRequest{ID: "id"}, nil
|
||||||
}
|
}
|
||||||
func (s *Storage) GetClientByClientID(id string) (op.Client, error) {
|
func (s *OPStorage) GetClientByClientID(id string) (op.Client, error) {
|
||||||
if id == "none" {
|
if id == "none" {
|
||||||
return nil, errors.New("not found")
|
return nil, errors.New("not found")
|
||||||
}
|
}
|
||||||
|
@ -97,19 +99,19 @@ func (s *Storage) GetClientByClientID(id string) (op.Client, error) {
|
||||||
}
|
}
|
||||||
return &ConfClient{applicationType: appType}, nil
|
return &ConfClient{applicationType: appType}, nil
|
||||||
}
|
}
|
||||||
func (s *Storage) AuthRequestByCode(op.Client, string, string) (op.AuthRequest, error) {
|
func (s *AuthStorage) AuthRequestByCode(op.Client, string, string) (op.AuthRequest, error) {
|
||||||
return &AuthRequest{ID: "native"}, nil
|
return &AuthRequest{ID: "native"}, nil
|
||||||
}
|
}
|
||||||
func (s *Storage) AuthorizeClientIDSecret(string, string) (op.Client, error) {
|
func (s *OPStorage) AuthorizeClientIDSecret(string, string) (op.Client, error) {
|
||||||
return &ConfClient{}, nil
|
return &ConfClient{}, nil
|
||||||
}
|
}
|
||||||
func (s *Storage) AuthorizeClientIDCodeVerifier(string, string) (op.Client, error) {
|
func (s *OPStorage) AuthorizeClientIDCodeVerifier(string, string) (op.Client, error) {
|
||||||
return &ConfClient{}, nil
|
return &ConfClient{}, nil
|
||||||
}
|
}
|
||||||
func (s *Storage) DeleteAuthRequestAndCode(string, string) error {
|
func (s *AuthStorage) DeleteAuthRequestAndCode(string, string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (s *Storage) AuthRequestByID(id string) (op.AuthRequest, error) {
|
func (s *AuthStorage) AuthRequestByID(id string) (op.AuthRequest, error) {
|
||||||
if id == "none" {
|
if id == "none" {
|
||||||
return nil, errors.New("not found")
|
return nil, errors.New("not found")
|
||||||
}
|
}
|
||||||
|
@ -127,10 +129,10 @@ func (s *Storage) AuthRequestByID(id string) (op.AuthRequest, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Storage) GetSigningKey() (*jose.SigningKey, error) {
|
func (s *AuthStorage) GetSigningKey() (*jose.SigningKey, error) {
|
||||||
return &jose.SigningKey{Algorithm: jose.RS256, Key: s.key}, nil
|
return &jose.SigningKey{Algorithm: jose.RS256, Key: s.key}, nil
|
||||||
}
|
}
|
||||||
func (s *Storage) GetKeySet() (jose.JSONWebKeySet, error) {
|
func (s *AuthStorage) GetKeySet() (jose.JSONWebKeySet, error) {
|
||||||
pubkey := s.key.Public()
|
pubkey := s.key.Public()
|
||||||
return jose.JSONWebKeySet{
|
return jose.JSONWebKeySet{
|
||||||
Keys: []jose.JSONWebKey{
|
Keys: []jose.JSONWebKey{
|
||||||
|
|
|
@ -15,8 +15,9 @@ func main() {
|
||||||
|
|
||||||
Port: "9998",
|
Port: "9998",
|
||||||
}
|
}
|
||||||
storage := mock.NewStorage()
|
authStorage := mock.NewAuthStorage()
|
||||||
handler, err := op.NewDefaultOP(config, storage, op.WithCustomTokenEndpoint("test"))
|
opStorage := &mock.OPStorage{}
|
||||||
|
handler, err := op.NewDefaultOP(config, authStorage, opStorage, op.WithCustomTokenEndpoint("test"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,11 @@ type Configuration interface {
|
||||||
TokenEndpoint() Endpoint
|
TokenEndpoint() Endpoint
|
||||||
UserinfoEndpoint() Endpoint
|
UserinfoEndpoint() Endpoint
|
||||||
KeysEndpoint() Endpoint
|
KeysEndpoint() Endpoint
|
||||||
|
|
||||||
|
// SupportedScopes() []string
|
||||||
|
AuthMethodBasicSupported() bool
|
||||||
|
AuthMethodPostSupported() bool
|
||||||
|
|
||||||
Port() string
|
Port() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,9 @@ const (
|
||||||
defaultIntrospectEndpoint = "introspect"
|
defaultIntrospectEndpoint = "introspect"
|
||||||
defaultUserinfoEndpoint = "userinfo"
|
defaultUserinfoEndpoint = "userinfo"
|
||||||
defaultKeysEndpoint = "keys"
|
defaultKeysEndpoint = "keys"
|
||||||
|
|
||||||
|
authMethodBasic = "client_secret_basic"
|
||||||
|
authMethodPost = "client_secret_post"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -94,19 +97,27 @@ func WithCustomUserinfoEndpoint(endpoint Endpoint) DefaultOPOpts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultOP(config *Config, storage Storage, opOpts ...DefaultOPOpts) (OpenIDProvider, error) {
|
func NewDefaultOP(config *Config, authStorage AuthStorage, opStorage OPStorage, opOpts ...DefaultOPOpts) (OpenIDProvider, error) {
|
||||||
err := ValidateIssuer(config.Issuer)
|
err := ValidateIssuer(config.Issuer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
storage := struct {
|
||||||
|
AuthStorage
|
||||||
|
OPStorage
|
||||||
|
}{
|
||||||
|
AuthStorage: authStorage,
|
||||||
|
OPStorage: opStorage,
|
||||||
|
}
|
||||||
|
|
||||||
p := &DefaultOP{
|
p := &DefaultOP{
|
||||||
config: config,
|
config: config,
|
||||||
storage: storage,
|
storage: storage,
|
||||||
endpoints: DefaultEndpoints,
|
endpoints: DefaultEndpoints,
|
||||||
}
|
}
|
||||||
|
|
||||||
p.signer, err = NewDefaultSigner(storage)
|
p.signer, err = NewDefaultSigner(authStorage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -117,7 +128,7 @@ func NewDefaultOP(config *Config, storage Storage, opOpts ...DefaultOPOpts) (Ope
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.discoveryConfig = CreateDiscoveryConfig(p)
|
p.discoveryConfig = CreateDiscoveryConfig(p, p.signer)
|
||||||
|
|
||||||
router := CreateRouter(p)
|
router := CreateRouter(p)
|
||||||
p.http = &http.Server{
|
p.http = &http.Server{
|
||||||
|
@ -152,6 +163,14 @@ func (p *DefaultOP) KeysEndpoint() Endpoint {
|
||||||
return Endpoint(p.endpoints.JwksURI)
|
return Endpoint(p.endpoints.JwksURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *DefaultOP) AuthMethodBasicSupported() bool {
|
||||||
|
return true //TODO: config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *DefaultOP) AuthMethodPostSupported() bool {
|
||||||
|
return true //TODO: config
|
||||||
|
}
|
||||||
|
|
||||||
func (p *DefaultOP) Port() string {
|
func (p *DefaultOP) Port() string {
|
||||||
return p.config.Port
|
return p.config.Port
|
||||||
}
|
}
|
||||||
|
@ -218,6 +237,8 @@ func (p *DefaultOP) HandleExchange(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *DefaultOP) handleTokenExchange(w http.ResponseWriter, r *http.Request) {
|
func (p *DefaultOP) handleTokenExchange(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ExchangeRequestError(w, r, ErrServerError("not implemented"))
|
||||||
|
return
|
||||||
tokenRequest, err := ParseTokenExchangeRequest(w, r)
|
tokenRequest, err := ParseTokenExchangeRequest(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//TODO: return err
|
//TODO: return err
|
||||||
|
|
|
@ -11,7 +11,7 @@ func Discover(w http.ResponseWriter, config *oidc.DiscoveryConfiguration) {
|
||||||
utils.MarshalJSON(w, config)
|
utils.MarshalJSON(w, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateDiscoveryConfig(c Configuration) *oidc.DiscoveryConfiguration {
|
func CreateDiscoveryConfig(c Configuration, s Signer) *oidc.DiscoveryConfiguration {
|
||||||
return &oidc.DiscoveryConfiguration{
|
return &oidc.DiscoveryConfiguration{
|
||||||
Issuer: c.Issuer(),
|
Issuer: c.Issuer(),
|
||||||
AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()),
|
AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()),
|
||||||
|
@ -21,13 +21,60 @@ func CreateDiscoveryConfig(c Configuration) *oidc.DiscoveryConfiguration {
|
||||||
// EndSessionEndpoint: c.TokenEndpoint().Absolute(c.Issuer())(c.EndSessionEndpoint),
|
// EndSessionEndpoint: c.TokenEndpoint().Absolute(c.Issuer())(c.EndSessionEndpoint),
|
||||||
// CheckSessionIframe: c.TokenEndpoint().Absolute(c.Issuer())(c.CheckSessionIframe),
|
// CheckSessionIframe: c.TokenEndpoint().Absolute(c.Issuer())(c.CheckSessionIframe),
|
||||||
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
|
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
|
||||||
// ScopesSupported: oidc.SupportedScopes,
|
ScopesSupported: scopes(c),
|
||||||
// ResponseTypesSupported: responseTypes,
|
ResponseTypesSupported: responseTypes(c),
|
||||||
// GrantTypesSupported: oidc.SupportedGrantTypes,
|
GrantTypesSupported: grantTypes(c),
|
||||||
// ClaimsSupported: oidc.SupportedClaims,
|
// ClaimsSupported: oidc.SupportedClaims,
|
||||||
// IdTokenSigningAlgValuesSupported: []string{keys.SigningAlgorithm},
|
IDTokenSigningAlgValuesSupported: sigAlgorithms(s),
|
||||||
// SubjectTypesSupported: []string{"public"},
|
SubjectTypesSupported: subjectTypes(c),
|
||||||
// TokenEndpointAuthMethodsSupported:
|
TokenEndpointAuthMethodsSupported: authMethods(c),
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func scopes(c Configuration) []string {
|
||||||
|
return []string{
|
||||||
|
"openid",
|
||||||
|
"profile",
|
||||||
|
"email",
|
||||||
|
"phone",
|
||||||
|
} //TODO: config
|
||||||
|
}
|
||||||
|
|
||||||
|
func responseTypes(c Configuration) []string {
|
||||||
|
return []string{
|
||||||
|
"code",
|
||||||
|
"id_token",
|
||||||
|
// "code token",
|
||||||
|
// "code id_token",
|
||||||
|
"id_token token",
|
||||||
|
// "code id_token token"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func grantTypes(c Configuration) []string {
|
||||||
|
return []string{
|
||||||
|
"client_credentials",
|
||||||
|
"authorization_code",
|
||||||
|
// "password",
|
||||||
|
"urn:ietf:params:oauth:grant-type:token-exchange",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sigAlgorithms(s Signer) []string {
|
||||||
|
return []string{string(s.SignatureAlgorithm())}
|
||||||
|
}
|
||||||
|
|
||||||
|
func subjectTypes(c Configuration) []string {
|
||||||
|
return []string{"public"} //TODO: config
|
||||||
|
}
|
||||||
|
|
||||||
|
func authMethods(c Configuration) []string {
|
||||||
|
authMethods := make([]string, 0, 2)
|
||||||
|
if c.AuthMethodBasicSupported() {
|
||||||
|
authMethods = append(authMethods, authMethodBasic)
|
||||||
|
}
|
||||||
|
if c.AuthMethodPostSupported() {
|
||||||
|
authMethods = append(authMethods, authMethodPost)
|
||||||
|
}
|
||||||
|
return authMethods
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,7 @@ func TestDiscover(t *testing.T) {
|
||||||
func TestCreateDiscoveryConfig(t *testing.T) {
|
func TestCreateDiscoveryConfig(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
c op.Configuration
|
c op.Configuration
|
||||||
|
s op.Signer
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -52,7 +53,7 @@ func TestCreateDiscoveryConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if got := op.CreateDiscoveryConfig(tt.args.c); !reflect.DeepEqual(got, tt.want) {
|
if got := op.CreateDiscoveryConfig(tt.args.c, tt.args.s); !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("CreateDiscoveryConfig() = %v, want %v", got, tt.want)
|
t.Errorf("CreateDiscoveryConfig() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -15,11 +15,11 @@ type Signer interface {
|
||||||
|
|
||||||
type idTokenSigner struct {
|
type idTokenSigner struct {
|
||||||
signer jose.Signer
|
signer jose.Signer
|
||||||
storage Storage
|
storage AuthStorage
|
||||||
algorithm jose.SignatureAlgorithm
|
algorithm jose.SignatureAlgorithm
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDefaultSigner(storage Storage) (Signer, error) {
|
func NewDefaultSigner(storage AuthStorage) (Signer, error) {
|
||||||
s := &idTokenSigner{
|
s := &idTokenSigner{
|
||||||
storage: storage,
|
storage: storage,
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,27 @@ import (
|
||||||
"github.com/caos/oidc/pkg/oidc"
|
"github.com/caos/oidc/pkg/oidc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Storage interface {
|
type AuthStorage interface {
|
||||||
CreateAuthRequest(*oidc.AuthRequest) (AuthRequest, error)
|
CreateAuthRequest(*oidc.AuthRequest) (AuthRequest, error)
|
||||||
GetClientByClientID(string) (Client, error)
|
|
||||||
AuthRequestByID(string) (AuthRequest, error)
|
AuthRequestByID(string) (AuthRequest, error)
|
||||||
AuthRequestByCode(Client, string, string) (AuthRequest, error)
|
AuthRequestByCode(Client, string, string) (AuthRequest, error)
|
||||||
AuthorizeClientIDSecret(string, string) (Client, error)
|
|
||||||
AuthorizeClientIDCodeVerifier(string, string) (Client, error)
|
|
||||||
DeleteAuthRequestAndCode(string, string) error
|
DeleteAuthRequestAndCode(string, string) error
|
||||||
|
|
||||||
GetSigningKey() (*jose.SigningKey, error)
|
GetSigningKey() (*jose.SigningKey, error)
|
||||||
GetKeySet() (jose.JSONWebKeySet, error)
|
GetKeySet() (jose.JSONWebKeySet, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OPStorage interface {
|
||||||
|
GetClientByClientID(string) (Client, error)
|
||||||
|
AuthorizeClientIDSecret(string, string) (Client, error)
|
||||||
|
AuthorizeClientIDCodeVerifier(string, string) (Client, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Storage interface {
|
||||||
|
AuthStorage
|
||||||
|
OPStorage
|
||||||
|
}
|
||||||
|
|
||||||
type AuthRequest interface {
|
type AuthRequest interface {
|
||||||
GetID() string
|
GetID() string
|
||||||
GetACR() string
|
GetACR() string
|
||||||
|
|
|
@ -17,6 +17,8 @@ type Exchanger interface {
|
||||||
Storage() Storage
|
Storage() Storage
|
||||||
Decoder() *schema.Decoder
|
Decoder() *schema.Decoder
|
||||||
Signer() Signer
|
Signer() Signer
|
||||||
|
AuthMethodBasicSupported() bool
|
||||||
|
AuthMethodPostSupported() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func CodeExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
|
func CodeExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
|
||||||
|
@ -37,7 +39,7 @@ func CodeExchange(w http.ResponseWriter, r *http.Request, exchanger Exchanger) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := AuthorizeClient(r, tokenReq, exchanger.Storage())
|
client, err := AuthorizeClient(r, tokenReq, exchanger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ExchangeRequestError(w, r, err)
|
ExchangeRequestError(w, r, err)
|
||||||
return
|
return
|
||||||
|
@ -99,19 +101,25 @@ func CreateIDToken(issuer string, authReq AuthRequest, validity time.Duration, a
|
||||||
return signer.SignIDToken(claims)
|
return signer.SignIDToken(claims)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AuthorizeClient(r *http.Request, tokenReq *oidc.AccessTokenRequest, storage Storage) (Client, error) {
|
func AuthorizeClient(r *http.Request, tokenReq *oidc.AccessTokenRequest, exchanger Exchanger) (Client, error) {
|
||||||
if tokenReq.ClientID == "" {
|
if tokenReq.ClientID == "" {
|
||||||
|
if !exchanger.AuthMethodBasicSupported() {
|
||||||
|
return nil, errors.New("basic not supported")
|
||||||
|
}
|
||||||
clientID, clientSecret, ok := r.BasicAuth()
|
clientID, clientSecret, ok := r.BasicAuth()
|
||||||
if ok {
|
if ok {
|
||||||
return storage.AuthorizeClientIDSecret(clientID, clientSecret)
|
return exchanger.Storage().AuthorizeClientIDSecret(clientID, clientSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if tokenReq.ClientSecret != "" {
|
if tokenReq.ClientSecret != "" {
|
||||||
return storage.AuthorizeClientIDSecret(tokenReq.ClientID, tokenReq.ClientSecret)
|
if !exchanger.AuthMethodPostSupported() {
|
||||||
|
return nil, errors.New("post not supported")
|
||||||
|
}
|
||||||
|
return exchanger.Storage().AuthorizeClientIDSecret(tokenReq.ClientID, tokenReq.ClientSecret)
|
||||||
}
|
}
|
||||||
if tokenReq.CodeVerifier != "" {
|
if tokenReq.CodeVerifier != "" {
|
||||||
return storage.AuthorizeClientIDCodeVerifier(tokenReq.ClientID, tokenReq.CodeVerifier)
|
return exchanger.Storage().AuthorizeClientIDCodeVerifier(tokenReq.ClientID, tokenReq.CodeVerifier)
|
||||||
}
|
}
|
||||||
return nil, errors.New("Unimplemented") //TODO: impl
|
return nil, errors.New("Unimplemented") //TODO: impl
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue