From ff2c164057ae862f48a7d36439c1446082c27ace Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=9D=A8=E6=96=87?= Date: Fri, 8 Oct 2021 14:20:45 +0800 Subject: [PATCH] fix: improve example & fix userinfo marshal (#132) * fix: example client should track state, call cli.CodeFlow need context * fix: oidc userinfo can UnmarshalJSON with address * rp Discover use client.Discover * add instruction for example to README.md --- README.md | 12 ++++++++++++ example/client/github/github.go | 2 +- example/internal/mock/storage.go | 7 ++++--- pkg/client/rp/relaying_party.go | 5 +++-- pkg/oidc/userinfo.go | 3 ++- pkg/oidc/userinfo_test.go | 26 ++++++++++++++++++++++++++ 6 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 pkg/oidc/userinfo_test.go diff --git a/README.md b/README.md index ef428ed..b90556a 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,18 @@ Whenever possible we tried to reuse / extend existing packages like `OAuth2 for Check the `/example` folder where example code for different scenarios is located. +```bash +# start oidc op server +# oidc discovery http://localhost:9998/.well-known/openid-configuration +CAOS_OIDC_DEV=1 go run github.com/caos/oidc/example/server/default +# start oidc web client +CLIENT_ID=web CLIENT_SECRET=web ISSUER=http://localhost:9998/ SCOPES=openid PORT=5556 go run github.com/caos/oidc/example/client/app +``` + +- browser http://localhost:5556/login will redirect to op server +- input id to login +- redirect to client app display user info + ## Features | | Code Flow | Implicit Flow | Hybrid Flow | Discovery | PKCE | Token Exchange | mTLS | JWT Profile | Refresh Token | diff --git a/example/client/github/github.go b/example/client/github/github.go index f39c40b..35c7723 100644 --- a/example/client/github/github.go +++ b/example/client/github/github.go @@ -43,7 +43,7 @@ func main() { state := func() string { return uuid.New().String() } - token := cli.CodeFlow(relyingParty, callbackPath, port, state) + token := cli.CodeFlow(ctx, relyingParty, callbackPath, port, state) client := github.NewClient(relyingParty.OAuthConfig().Client(ctx, token.Token)) diff --git a/example/internal/mock/storage.go b/example/internal/mock/storage.go index 214ba54..775f757 100644 --- a/example/internal/mock/storage.go +++ b/example/internal/mock/storage.go @@ -36,6 +36,7 @@ type AuthRequest struct { Nonce string ClientID string CodeChallenge *oidc.CodeChallenge + State string } func (a *AuthRequest) GetACR() string { @@ -98,7 +99,7 @@ func (a *AuthRequest) GetScopes() []string { func (a *AuthRequest) SetCurrentScopes(scopes []string) {} func (a *AuthRequest) GetState() string { - return "" + return a.State } func (a *AuthRequest) GetSubject() string { @@ -120,7 +121,7 @@ func (s *AuthStorage) Health(ctx context.Context) error { } func (s *AuthStorage) CreateAuthRequest(_ context.Context, authReq *oidc.AuthRequest, _ string) (op.AuthRequest, error) { - a = &AuthRequest{ID: "id", ClientID: authReq.ClientID, ResponseType: authReq.ResponseType, Nonce: authReq.Nonce, RedirectURI: authReq.RedirectURI} + a = &AuthRequest{ID: "id", ClientID: authReq.ClientID, ResponseType: authReq.ResponseType, Nonce: authReq.Nonce, RedirectURI: authReq.RedirectURI, State: authReq.State} if authReq.CodeChallenge != "" { a.CodeChallenge = &oidc.CodeChallenge{ Challenge: authReq.CodeChallenge, @@ -212,7 +213,7 @@ func (s *AuthStorage) GetClientByClientID(_ context.Context, id string) (op.Clie accessTokenType = op.AccessTokenTypeJWT responseTypes = []oidc.ResponseType{oidc.ResponseTypeIDToken, oidc.ResponseTypeIDTokenOnly} } - return &ConfClient{ID: id, applicationType: appType, authMethod: authMethod, accessTokenType: accessTokenType, responseTypes: responseTypes, devMode: false}, nil + return &ConfClient{ID: id, applicationType: appType, authMethod: authMethod, accessTokenType: accessTokenType, responseTypes: responseTypes, devMode: false, grantTypes: []oidc.GrantType{oidc.GrantTypeCode}}, nil } func (s *AuthStorage) AuthorizeClientIDSecret(_ context.Context, id string, _ string) error { diff --git a/pkg/client/rp/relaying_party.go b/pkg/client/rp/relaying_party.go index 669a910..b9b568d 100644 --- a/pkg/client/rp/relaying_party.go +++ b/pkg/client/rp/relaying_party.go @@ -170,17 +170,18 @@ func NewRelyingPartyOIDC(issuer, clientID, clientSecret, redirectURI string, sco return nil, err } } - endpoints, err := Discover(rp.issuer, rp.httpClient) + discoveryConfiguration, err := client.Discover(rp.issuer, rp.httpClient) if err != nil { return nil, err } + endpoints := GetEndpoints(discoveryConfiguration) rp.oauthConfig.Endpoint = endpoints.Endpoint rp.endpoints = endpoints return rp, nil } -//DefaultRPOpts is the type for providing dynamic options to the DefaultRP +//Option is the type for providing dynamic options to the DefaultRP type Option func(*relyingParty) error //WithCookieHandler set a `CookieHandler` for securing the various redirects diff --git a/pkg/oidc/userinfo.go b/pkg/oidc/userinfo.go index 6bc0016..2ae2acb 100644 --- a/pkg/oidc/userinfo.go +++ b/pkg/oidc/userinfo.go @@ -360,6 +360,7 @@ func (i *userinfo) MarshalJSON() ([]byte, error) { func (i *userinfo) UnmarshalJSON(data []byte) error { type Alias userinfo a := &struct { + Address *userInfoAddress `json:"address,omitempty"` *Alias UpdatedAt int64 `json:"update_at,omitempty"` }{ @@ -368,7 +369,7 @@ func (i *userinfo) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &a); err != nil { return err } - + i.Address = a.Address i.UpdatedAt = Time(time.Unix(a.UpdatedAt, 0).UTC()) if err := json.Unmarshal(data, &i.claims); err != nil { diff --git a/pkg/oidc/userinfo_test.go b/pkg/oidc/userinfo_test.go new file mode 100644 index 0000000..c3c8b7b --- /dev/null +++ b/pkg/oidc/userinfo_test.go @@ -0,0 +1,26 @@ +package oidc + +import ( + "encoding/json" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestUserInfoMarshal(t *testing.T) { + userinfo := NewUserInfo() + userinfo.SetSubject("test") + userinfo.SetAddress(NewUserInfoAddress("Test 789\nPostfach 2", "", "", "", "", "")) + userinfo.SetEmail("test", true) + userinfo.SetPhone("0791234567", true) + userinfo.SetName("Test") + userinfo.AppendClaims("private_claim", "test") + + marshal, err := json.Marshal(userinfo) + out := NewUserInfo() + assert.NoError(t, err) + assert.NoError(t, json.Unmarshal(marshal, out)) + assert.Equal(t, userinfo.GetAddress(), out.GetAddress()) + expected, err := json.Marshal(out) + assert.NoError(t, err) + assert.Equal(t, expected, marshal) +}