(discovery) config and storage

This commit is contained in:
Livio Amstutz 2019-12-03 15:17:06 +01:00
parent ce6f3182a2
commit ecea7e3730
9 changed files with 132 additions and 38 deletions

View file

@ -12,18 +12,20 @@ import (
"github.com/caos/oidc/pkg/op"
)
type Storage struct {
type AuthStorage struct {
key *rsa.PrivateKey
}
func NewStorage() op.Storage {
type OPStorage struct{}
func NewAuthStorage() op.AuthStorage {
reader := rand.Reader
bitSize := 2048
key, err := rsa.GenerateKey(reader, bitSize)
if err != nil {
panic(err)
}
return &Storage{
return &AuthStorage{
key: key,
}
}
@ -80,10 +82,10 @@ func (a *AuthRequest) GetSubject() string {
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
}
func (s *Storage) GetClientByClientID(id string) (op.Client, error) {
func (s *OPStorage) GetClientByClientID(id string) (op.Client, error) {
if id == "none" {
return nil, errors.New("not found")
}
@ -97,19 +99,19 @@ func (s *Storage) GetClientByClientID(id string) (op.Client, error) {
}
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
}
func (s *Storage) AuthorizeClientIDSecret(string, string) (op.Client, error) {
func (s *OPStorage) AuthorizeClientIDSecret(string, string) (op.Client, error) {
return &ConfClient{}, nil
}
func (s *Storage) AuthorizeClientIDCodeVerifier(string, string) (op.Client, error) {
func (s *OPStorage) AuthorizeClientIDCodeVerifier(string, string) (op.Client, error) {
return &ConfClient{}, nil
}
func (s *Storage) DeleteAuthRequestAndCode(string, string) error {
func (s *AuthStorage) DeleteAuthRequestAndCode(string, string) error {
return nil
}
func (s *Storage) AuthRequestByID(id string) (op.AuthRequest, error) {
func (s *AuthStorage) AuthRequestByID(id string) (op.AuthRequest, error) {
if id == "none" {
return nil, errors.New("not found")
}
@ -127,10 +129,10 @@ func (s *Storage) AuthRequestByID(id string) (op.AuthRequest, error) {
}, 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
}
func (s *Storage) GetKeySet() (jose.JSONWebKeySet, error) {
func (s *AuthStorage) GetKeySet() (jose.JSONWebKeySet, error) {
pubkey := s.key.Public()
return jose.JSONWebKeySet{
Keys: []jose.JSONWebKey{

View file

@ -15,8 +15,9 @@ func main() {
Port: "9998",
}
storage := mock.NewStorage()
handler, err := op.NewDefaultOP(config, storage, op.WithCustomTokenEndpoint("test"))
authStorage := mock.NewAuthStorage()
opStorage := &mock.OPStorage{}
handler, err := op.NewDefaultOP(config, authStorage, opStorage, op.WithCustomTokenEndpoint("test"))
if err != nil {
log.Fatal(err)
}

View file

@ -12,6 +12,11 @@ type Configuration interface {
TokenEndpoint() Endpoint
UserinfoEndpoint() Endpoint
KeysEndpoint() Endpoint
// SupportedScopes() []string
AuthMethodBasicSupported() bool
AuthMethodPostSupported() bool
Port() string
}

View file

@ -15,6 +15,9 @@ const (
defaultIntrospectEndpoint = "introspect"
defaultUserinfoEndpoint = "userinfo"
defaultKeysEndpoint = "keys"
authMethodBasic = "client_secret_basic"
authMethodPost = "client_secret_post"
)
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)
if err != nil {
return nil, err
}
storage := struct {
AuthStorage
OPStorage
}{
AuthStorage: authStorage,
OPStorage: opStorage,
}
p := &DefaultOP{
config: config,
storage: storage,
endpoints: DefaultEndpoints,
}
p.signer, err = NewDefaultSigner(storage)
p.signer, err = NewDefaultSigner(authStorage)
if err != nil {
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)
p.http = &http.Server{
@ -152,6 +163,14 @@ func (p *DefaultOP) KeysEndpoint() Endpoint {
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 {
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) {
ExchangeRequestError(w, r, ErrServerError("not implemented"))
return
tokenRequest, err := ParseTokenExchangeRequest(w, r)
if err != nil {
//TODO: return err

View file

@ -11,7 +11,7 @@ func Discover(w http.ResponseWriter, config *oidc.DiscoveryConfiguration) {
utils.MarshalJSON(w, config)
}
func CreateDiscoveryConfig(c Configuration) *oidc.DiscoveryConfiguration {
func CreateDiscoveryConfig(c Configuration, s Signer) *oidc.DiscoveryConfiguration {
return &oidc.DiscoveryConfiguration{
Issuer: c.Issuer(),
AuthorizationEndpoint: c.AuthorizationEndpoint().Absolute(c.Issuer()),
@ -20,14 +20,61 @@ func CreateDiscoveryConfig(c Configuration) *oidc.DiscoveryConfiguration {
UserinfoEndpoint: c.UserinfoEndpoint().Absolute(c.Issuer()),
// EndSessionEndpoint: c.TokenEndpoint().Absolute(c.Issuer())(c.EndSessionEndpoint),
// CheckSessionIframe: c.TokenEndpoint().Absolute(c.Issuer())(c.CheckSessionIframe),
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
// ScopesSupported: oidc.SupportedScopes,
// ResponseTypesSupported: responseTypes,
// GrantTypesSupported: oidc.SupportedGrantTypes,
JwksURI: c.KeysEndpoint().Absolute(c.Issuer()),
ScopesSupported: scopes(c),
ResponseTypesSupported: responseTypes(c),
GrantTypesSupported: grantTypes(c),
// ClaimsSupported: oidc.SupportedClaims,
// IdTokenSigningAlgValuesSupported: []string{keys.SigningAlgorithm},
// SubjectTypesSupported: []string{"public"},
// TokenEndpointAuthMethodsSupported:
IDTokenSigningAlgValuesSupported: sigAlgorithms(s),
SubjectTypesSupported: subjectTypes(c),
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
}

View file

@ -42,6 +42,7 @@ func TestDiscover(t *testing.T) {
func TestCreateDiscoveryConfig(t *testing.T) {
type args struct {
c op.Configuration
s op.Signer
}
tests := []struct {
name string
@ -52,7 +53,7 @@ func TestCreateDiscoveryConfig(t *testing.T) {
}
for _, tt := range tests {
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)
}
})

View file

@ -15,11 +15,11 @@ type Signer interface {
type idTokenSigner struct {
signer jose.Signer
storage Storage
storage AuthStorage
algorithm jose.SignatureAlgorithm
}
func NewDefaultSigner(storage Storage) (Signer, error) {
func NewDefaultSigner(storage AuthStorage) (Signer, error) {
s := &idTokenSigner{
storage: storage,
}

View file

@ -8,18 +8,27 @@ import (
"github.com/caos/oidc/pkg/oidc"
)
type Storage interface {
type AuthStorage interface {
CreateAuthRequest(*oidc.AuthRequest) (AuthRequest, error)
GetClientByClientID(string) (Client, error)
AuthRequestByID(string) (AuthRequest, error)
AuthRequestByCode(Client, string, string) (AuthRequest, error)
AuthorizeClientIDSecret(string, string) (Client, error)
AuthorizeClientIDCodeVerifier(string, string) (Client, error)
DeleteAuthRequestAndCode(string, string) error
GetSigningKey() (*jose.SigningKey, 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 {
GetID() string
GetACR() string

View file

@ -17,6 +17,8 @@ type Exchanger interface {
Storage() Storage
Decoder() *schema.Decoder
Signer() Signer
AuthMethodBasicSupported() bool
AuthMethodPostSupported() bool
}
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
}
client, err := AuthorizeClient(r, tokenReq, exchanger.Storage())
client, err := AuthorizeClient(r, tokenReq, exchanger)
if err != nil {
ExchangeRequestError(w, r, err)
return
@ -99,19 +101,25 @@ func CreateIDToken(issuer string, authReq AuthRequest, validity time.Duration, a
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 !exchanger.AuthMethodBasicSupported() {
return nil, errors.New("basic not supported")
}
clientID, clientSecret, ok := r.BasicAuth()
if ok {
return storage.AuthorizeClientIDSecret(clientID, clientSecret)
return exchanger.Storage().AuthorizeClientIDSecret(clientID, 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 != "" {
return storage.AuthorizeClientIDCodeVerifier(tokenReq.ClientID, tokenReq.CodeVerifier)
return exchanger.Storage().AuthorizeClientIDCodeVerifier(tokenReq.ClientID, tokenReq.CodeVerifier)
}
return nil, errors.New("Unimplemented") //TODO: impl
}