docs(example): implement OpenID Provider (#165)

* chore(example): implement OpenID Provider

* jwt profile and fixes

* some comments

* remove old op example

* fix code flow example

* add service user and update readme

* fix password for example use

* ignore example and mock folders for code coverage

* Update example/server/internal/storage.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update client.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
Livio Amstutz 2022-04-21 17:54:00 +02:00 committed by GitHub
parent c195452bb0
commit 885fe0d45c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 1280 additions and 427 deletions

View file

@ -0,0 +1,203 @@
package internal
import (
"time"
"golang.org/x/text/language"
"github.com/caos/oidc/pkg/op"
"github.com/caos/oidc/pkg/oidc"
)
const (
//CustomScope is an example for how to use custom scopes in this library
//(in this scenario, when requested, it will return a custom claim)
CustomScope = "custom_scope"
//CustomClaim is an example for how to return custom claims with this library
CustomClaim = "custom_claim"
)
type AuthRequest struct {
ID string
CreationDate time.Time
ApplicationID string
CallbackURI string
TransferState string
Prompt []string
UiLocales []language.Tag
LoginHint string
MaxAuthAge *time.Duration
UserID string
Scopes []string
ResponseType oidc.ResponseType
Nonce string
CodeChallenge *OIDCCodeChallenge
passwordChecked bool
authTime time.Time
}
func (a *AuthRequest) GetID() string {
return a.ID
}
func (a *AuthRequest) GetACR() string {
return "" //we won't handle acr in this example
}
func (a *AuthRequest) GetAMR() []string {
//this example only uses password for authentication
if a.passwordChecked {
return []string{"pwd"}
}
return nil
}
func (a *AuthRequest) GetAudience() []string {
return []string{a.ApplicationID} //this example will always just use the client_id as audience
}
func (a *AuthRequest) GetAuthTime() time.Time {
return a.authTime
}
func (a *AuthRequest) GetClientID() string {
return a.ApplicationID
}
func (a *AuthRequest) GetCodeChallenge() *oidc.CodeChallenge {
return CodeChallengeToOIDC(a.CodeChallenge)
}
func (a *AuthRequest) GetNonce() string {
return a.Nonce
}
func (a *AuthRequest) GetRedirectURI() string {
return a.CallbackURI
}
func (a *AuthRequest) GetResponseType() oidc.ResponseType {
return a.ResponseType
}
func (a *AuthRequest) GetResponseMode() oidc.ResponseMode {
return "" //we won't handle response mode in this example
}
func (a *AuthRequest) GetScopes() []string {
return a.Scopes
}
func (a *AuthRequest) GetState() string {
return a.TransferState
}
func (a *AuthRequest) GetSubject() string {
return a.UserID
}
func (a *AuthRequest) Done() bool {
return a.passwordChecked //this example only uses password for authentication
}
func PromptToInternal(oidcPrompt oidc.SpaceDelimitedArray) []string {
prompts := make([]string, len(oidcPrompt))
for _, oidcPrompt := range oidcPrompt {
switch oidcPrompt {
case oidc.PromptNone,
oidc.PromptLogin,
oidc.PromptConsent,
oidc.PromptSelectAccount:
prompts = append(prompts, oidcPrompt)
}
}
return prompts
}
func MaxAgeToInternal(maxAge *uint) *time.Duration {
if maxAge == nil {
return nil
}
dur := time.Duration(*maxAge) * time.Second
return &dur
}
func authRequestToInternal(authReq *oidc.AuthRequest, userID string) *AuthRequest {
return &AuthRequest{
CreationDate: time.Now(),
ApplicationID: authReq.ClientID,
CallbackURI: authReq.RedirectURI,
TransferState: authReq.State,
Prompt: PromptToInternal(authReq.Prompt),
UiLocales: authReq.UILocales,
LoginHint: authReq.LoginHint,
MaxAuthAge: MaxAgeToInternal(authReq.MaxAge),
UserID: userID,
Scopes: authReq.Scopes,
ResponseType: authReq.ResponseType,
Nonce: authReq.Nonce,
CodeChallenge: &OIDCCodeChallenge{
Challenge: authReq.CodeChallenge,
Method: string(authReq.CodeChallengeMethod),
},
}
}
type OIDCCodeChallenge struct {
Challenge string
Method string
}
func CodeChallengeToOIDC(challenge *OIDCCodeChallenge) *oidc.CodeChallenge {
if challenge == nil {
return nil
}
challengeMethod := oidc.CodeChallengeMethodPlain
if challenge.Method == "S256" {
challengeMethod = oidc.CodeChallengeMethodS256
}
return &oidc.CodeChallenge{
Challenge: challenge.Challenge,
Method: challengeMethod,
}
}
//RefreshTokenRequestFromBusiness will simply wrap the internal RefreshToken to implement the op.RefreshTokenRequest interface
func RefreshTokenRequestFromBusiness(token *RefreshToken) op.RefreshTokenRequest {
return &RefreshTokenRequest{token}
}
type RefreshTokenRequest struct {
*RefreshToken
}
func (r *RefreshTokenRequest) GetAMR() []string {
return r.AMR
}
func (r *RefreshTokenRequest) GetAudience() []string {
return r.Audience
}
func (r *RefreshTokenRequest) GetAuthTime() time.Time {
return r.AuthTime
}
func (r *RefreshTokenRequest) GetClientID() string {
return r.ApplicationID
}
func (r *RefreshTokenRequest) GetScopes() []string {
return r.Scopes
}
func (r *RefreshTokenRequest) GetSubject() string {
return r.UserID
}
func (r *RefreshTokenRequest) SetCurrentScopes(scopes []string) {
r.Scopes = scopes
}