zitadel-oidc/example/server/internal/client.go
Livio Amstutz 885fe0d45c
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>
2022-04-21 17:54:00 +02:00

189 lines
6.9 KiB
Go

package internal
import (
"time"
"github.com/caos/oidc/pkg/oidc"
"github.com/caos/oidc/pkg/op"
)
var (
//we use the default login UI and pass the (auth request) id
defaultLoginURL = func(id string) string {
return "/login/username?authRequestID=" + id
}
//clients to be used by the storage interface
clients = map[string]*Client{}
)
//Client represents the internal model of an OAuth/OIDC client
//this could also be your database model
type Client struct {
id string
secret string
redirectURIs []string
applicationType op.ApplicationType
authMethod oidc.AuthMethod
loginURL func(string) string
responseTypes []oidc.ResponseType
grantTypes []oidc.GrantType
accessTokenType op.AccessTokenType
devMode bool
idTokenUserinfoClaimsAssertion bool
clockSkew time.Duration
}
//GetID must return the client_id
func (c *Client) GetID() string {
return c.id
}
//RedirectURIs must return the registered redirect_uris for Code and Implicit Flow
func (c *Client) RedirectURIs() []string {
return c.redirectURIs
}
//PostLogoutRedirectURIs must return the registered post_logout_redirect_uris for sign-outs
func (c *Client) PostLogoutRedirectURIs() []string {
return []string{}
}
//ApplicationType must return the type of the client (app, native, user agent)
func (c *Client) ApplicationType() op.ApplicationType {
return c.applicationType
}
//AuthMethod must return the authentication method (client_secret_basic, client_secret_post, none, private_key_jwt)
func (c *Client) AuthMethod() oidc.AuthMethod {
return c.authMethod
}
//ResponseTypes must return all allowed response types (code, id_token token, id_token)
//these must match with the allowed grant types
func (c *Client) ResponseTypes() []oidc.ResponseType {
return c.responseTypes
}
//GrantTypes must return all allowed grant types (authorization_code, refresh_token, urn:ietf:params:oauth:grant-type:jwt-bearer)
func (c *Client) GrantTypes() []oidc.GrantType {
return c.grantTypes
}
//LoginURL will be called to redirect the user (agent) to the login UI
//you could implement some logic here to redirect the users to different login UIs depending on the client
func (c *Client) LoginURL(id string) string {
return c.loginURL(id)
}
//AccessTokenType must return the type of access token the client uses (Bearer (opaque) or JWT)
func (c *Client) AccessTokenType() op.AccessTokenType {
return c.accessTokenType
}
//IDTokenLifetime must return the lifetime of the client's id_tokens
func (c *Client) IDTokenLifetime() time.Duration {
return 1 * time.Hour
}
//DevMode enables the use of non-compliant configs such as redirect_uris (e.g. http schema for user agent client)
func (c *Client) DevMode() bool {
return c.devMode
}
//RestrictAdditionalIdTokenScopes allows specifying which custom scopes shall be asserted into the id_token
func (c *Client) RestrictAdditionalIdTokenScopes() func(scopes []string) []string {
return func(scopes []string) []string {
return scopes
}
}
//RestrictAdditionalAccessTokenScopes allows specifying which custom scopes shall be asserted into the JWT access_token
func (c *Client) RestrictAdditionalAccessTokenScopes() func(scopes []string) []string {
return func(scopes []string) []string {
return scopes
}
}
//IsScopeAllowed enables Client specific custom scopes validation
//in this example we allow the CustomScope for all clients
func (c *Client) IsScopeAllowed(scope string) bool {
return scope == CustomScope
}
//IDTokenUserinfoClaimsAssertion allows specifying if claims of scope profile, email, phone and address are asserted into the id_token
//even if an access token if issued which violates the OIDC Core spec
//(5.4. Requesting Claims using Scope Values: https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims)
//some clients though require that e.g. email is always in the id_token when requested even if an access_token is issued
func (c *Client) IDTokenUserinfoClaimsAssertion() bool {
return c.idTokenUserinfoClaimsAssertion
}
//ClockSkew enables clients to instruct the OP to apply a clock skew on the various times and expirations
//(subtract from issued_at, add to expiration, ...)
func (c *Client) ClockSkew() time.Duration {
return c.clockSkew
}
//RegisterClients enables you to register clients for the example implementation
//there are some clients (web and native) to try out different cases
//add more if necessary
func RegisterClients(registerClients ...*Client) {
for _, client := range registerClients {
clients[client.id] = client
}
}
//NativeClient will create a client of type native, which will always use PKCE and allow the use of refresh tokens
//user-defined redirectURIs may include:
// - http://localhost without port specification (e.g. http://localhost/auth/callback)
// - custom protocol (e.g. custom://auth/callback)
//(the examples will be used as default, if none is provided)
func NativeClient(id string, redirectURIs ...string) *Client {
if len(redirectURIs) == 0 {
redirectURIs = []string{
"http://localhost/auth/callback",
"custom://auth/callback",
}
}
return &Client{
id: id,
secret: "", //no secret needed (due to PKCE)
redirectURIs: redirectURIs,
applicationType: op.ApplicationTypeNative,
authMethod: oidc.AuthMethodNone,
loginURL: defaultLoginURL,
responseTypes: []oidc.ResponseType{oidc.ResponseTypeCode},
grantTypes: []oidc.GrantType{oidc.GrantTypeCode, oidc.GrantTypeRefreshToken},
accessTokenType: 0,
devMode: false,
idTokenUserinfoClaimsAssertion: false,
clockSkew: 0,
}
}
//WebClient will create a client of type web, which will always use Basic Auth and allow the use of refresh tokens
//user-defined redirectURIs may include:
// - http://localhost with port specification (e.g. http://localhost:9999/auth/callback)
//(the example will be used as default, if none is provided)
func WebClient(id, secret string, redirectURIs ...string) *Client {
if len(redirectURIs) == 0 {
redirectURIs = []string{
"http://localhost:9999/auth/callback",
}
}
return &Client{
id: id,
secret: secret,
redirectURIs: redirectURIs,
applicationType: op.ApplicationTypeWeb,
authMethod: oidc.AuthMethodBasic,
loginURL: defaultLoginURL,
responseTypes: []oidc.ResponseType{oidc.ResponseTypeCode},
grantTypes: []oidc.GrantType{oidc.GrantTypeCode, oidc.GrantTypeRefreshToken},
accessTokenType: 0,
devMode: false,
idTokenUserinfoClaimsAssertion: false,
clockSkew: 0,
}
}