Merge branch 'main' into op

# Conflicts:
#	pkg/client/rp/relaying_party.go
This commit is contained in:
Livio Amstutz 2021-10-28 07:51:28 +02:00
commit cef977adc2
8 changed files with 95 additions and 11 deletions

View file

@ -2,9 +2,12 @@ name: Release
on:
push:
branches:
- '**'
- main
tags-ignore:
- '**'
pull_request:
branches:
- '**'
workflow_dispatch:
jobs:

View file

@ -21,11 +21,23 @@ 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 |
|----------------|-----------|---------------|-------------|-----------|------|----------------|---------|-------------|---------------|
| Relaying Party | yes | yes | not yet | yes | yes | partial | not yet | yes | yes |
| Relaying Party | yes | no[^1] | no | yes | yes | partial | not yet | yes | yes |
| Origin Party | yes | yes | not yet | yes | yes | not yet | not yet | yes | yes |
### Resources
@ -56,7 +68,7 @@ As of 2020 there are not a lot of `OIDC` library's in `Go` which can handle serv
- [Certify this library as OP](https://openid.net/certification/#OPs)
### Other Go OpenID Connect library's
### Other Go OpenID Connect libraries
[https://github.com/coreos/go-oidc](https://github.com/coreos/go-oidc)
@ -64,7 +76,7 @@ The `go-oidc` does only support `RP` and is not feasible to use as `OP` that's w
[https://github.com/ory/fosite](https://github.com/ory/fosite)
We did not choose `fosite` because it implements `OAuth 2.0` on its own and does not rely in the golang provided package. Nonetheless this is a great project.
We did not choose `fosite` because it implements `OAuth 2.0` on its own and does not rely on the golang provided package. Nonetheless this is a great project.
## License
@ -73,3 +85,6 @@ The full functionality of this library is and stays open source and free to use
See the exact licensing terms [here](./LICENSE)
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
[^1]: https://github.com/caos/oidc/issues/135#issuecomment-950563892

View file

@ -37,6 +37,7 @@ type AuthRequest struct {
Nonce string
ClientID string
CodeChallenge *oidc.CodeChallenge
State string
}
func (a *AuthRequest) GetACR() string {
@ -103,7 +104,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 {
@ -125,7 +126,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,
@ -217,7 +218,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 {

View file

@ -170,11 +170,11 @@ func NewRelyingPartyOIDC(issuer, clientID, clientSecret, redirectURI string, sco
return nil, err
}
}
config, err := client.Discover(rp.issuer, rp.httpClient)
discoveryConfiguration, err := client.Discover(rp.issuer, rp.httpClient)
if err != nil {
return nil, err
}
endpoints := GetEndpoints(config)
endpoints := GetEndpoints(discoveryConfiguration)
rp.oauthConfig.Endpoint = endpoints.Endpoint
rp.endpoints = endpoints

View file

@ -27,6 +27,14 @@ func ConcatenateJSON(first, second []byte) ([]byte, error) {
if !bytes.HasPrefix(second, []byte{'{'}) {
return nil, fmt.Errorf("jws: invalid JSON %s", second)
}
// check empty
if len(first) == 2 {
return second, nil
}
if len(second) == 2 {
return first, nil
}
first[len(first)-1] = ','
first = append(first, second[1:]...)
return first, nil

View file

@ -44,6 +44,36 @@ func TestConcatenateJSON(t *testing.T) {
[]byte(`{"some": "thing","another": "thing"}`),
false,
},
{
"first empty",
args{
[]byte(`{}`),
[]byte(`{"some": "thing"}`),
},
[]byte(`{"some": "thing"}`),
false,
},
{
"second empty",
args{
[]byte(`{"some": "thing"}`),
[]byte(`{}`),
},
[]byte(`{"some": "thing"}`),
false,
},
{
"both empty",
args{
[]byte(`{}`),
[]byte(`{}`),
},
[]byte(`{}`),
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -53,7 +83,7 @@ func TestConcatenateJSON(t *testing.T) {
return
}
if !bytes.Equal(got, tt.want) {
t.Errorf("ConcatenateJSON() got = %v, want %v", got, tt.want)
t.Errorf("ConcatenateJSON() got = %v, want %v", string(got), tt.want)
}
})
}

View file

@ -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 {

26
pkg/oidc/userinfo_test.go Normal file
View file

@ -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)
}