(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"
|
||||
)
|
||||
|
||||
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{
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -12,6 +12,11 @@ type Configuration interface {
|
|||
TokenEndpoint() Endpoint
|
||||
UserinfoEndpoint() Endpoint
|
||||
KeysEndpoint() Endpoint
|
||||
|
||||
// SupportedScopes() []string
|
||||
AuthMethodBasicSupported() bool
|
||||
AuthMethodPostSupported() bool
|
||||
|
||||
Port() string
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue