Skip to content

Commit

Permalink
feat: Open ID 2.0 strategy
Browse files Browse the repository at this point in the history
- Registration strategy until redirect to external provider
  • Loading branch information
tbureck committed Feb 17, 2024
1 parent 72bdeda commit fd0e69a
Show file tree
Hide file tree
Showing 21 changed files with 959 additions and 1 deletion.
5 changes: 5 additions & 0 deletions driver/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ const (
ViperKeyIgnoreNetworkErrors = "selfservice.methods.password.config.ignore_network_errors"
ViperKeyTOTPIssuer = "selfservice.methods.totp.config.issuer"
ViperKeyOIDCBaseRedirectURL = "selfservice.methods.oidc.config.base_redirect_uri"
ViperKeyOid2BaseRedirectURL = "selfservice.methods.oid2.config.base_redirect_uri"
ViperKeyWebAuthnRPDisplayName = "selfservice.methods.webauthn.config.rp.display_name"
ViperKeyWebAuthnRPID = "selfservice.methods.webauthn.config.rp.id"
ViperKeyWebAuthnRPOrigin = "selfservice.methods.webauthn.config.rp.origin"
Expand Down Expand Up @@ -590,6 +591,10 @@ func (p *Config) OIDCRedirectURIBase(ctx context.Context) *url.URL {
return p.GetProvider(ctx).URIF(ViperKeyOIDCBaseRedirectURL, p.SelfPublicURL(ctx))
}

func (p *Config) Oid2RedirectURIBase(ctx context.Context) *url.URL {
return p.GetProvider(ctx).URIF(ViperKeyOid2BaseRedirectURL, p.SelfPublicURL(ctx))
}

func (p *Config) IdentityTraitsSchemas(ctx context.Context) (ss Schemas, err error) {
if err = p.GetProvider(ctx).Koanf.Unmarshal(ViperKeyIdentitySchemas, &ss); err != nil {
return ss, nil
Expand Down
2 changes: 2 additions & 0 deletions driver/registry_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package driver
import (
"context"
"crypto/sha256"
"github.com/ory/kratos/selfservice/strategy/oid2"
"net/http"
"strings"
"sync"
Expand Down Expand Up @@ -313,6 +314,7 @@ func (m *RegistryDefault) selfServiceStrategies() []any {
// Construct the default list of strategies
m.selfserviceStrategies = []any{
password.NewStrategy(m),
oid2.NewStrategy(m),
oidc.NewStrategy(m),
profile.NewStrategy(m),
code.NewStrategy(m),
Expand Down
75 changes: 75 additions & 0 deletions embedx/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,45 @@
}
]
},
"selfServiceOid2Provider": {
"type": "object",
"properties": {
"id": {
"type": "string",
"examples": [
"steam"
]
},
"provider": {
"title": "Provider",
"description": "Can be one of generic, steam.",
"type": "string",
"enum": [
"generic",
"steam"
],
"examples": [
"steam"
]
},
"label": {
"title": "Optional string which will be used when generating labels for UI buttons.",
"type": "string"
},
"discovery_url": {
"type": "string",
"format": "uri",
"examples": [
"https://accounts.google.com/o/oauth2/v2/auth"
]
}
},
"additionalProperties": false,
"required": [
"id",
"provider"
]
},
"selfServiceHooks": {
"type": "array",
"items": {
Expand Down Expand Up @@ -1721,6 +1760,42 @@
}
}
}
},
"oid2": {
"type": "object",
"title": "Specify OpenID 2.0 Configuration",
"showEnvVarBlockForObject": true,
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"title": "Enables OpenID 2.0 Method",
"default": false
},
"config": {
"type": "object",
"additionalProperties": false,
"properties": {
"base_redirect_uri": {
"type": "string",
"title": "Base URL for OpenID 2.0 Redirect URIs",
"description": "Can be used to modify the base URL for OpenID 2.0 Redirect URLs. If unset, the Public Base URL will be used.",
"format": "uri",
"examples": [
"https://auth.myexample.org/"
]
},
"providers": {
"title": "OpenID 2.0 Providers",
"description": "A list and configuration of OpenID 2.0 providers Ory Kratos should integrate with.",
"type": "array",
"items": {
"$ref": "#/definitions/selfServiceOid2Provider"
}
}
}
}
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ require (
github.com/tidwall/gjson v1.14.3
github.com/tidwall/sjson v1.2.5
github.com/urfave/negroni v1.0.0
github.com/yohcop/openid-go v1.0.1
github.com/zmb3/spotify/v2 v2.4.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0
go.opentelemetry.io/otel v1.22.0
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,8 @@ github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -1109,6 +1111,8 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
Expand Down Expand Up @@ -1205,6 +1209,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
Expand Down Expand Up @@ -1321,6 +1327,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20191110171634-ad39bd3f0407/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand All @@ -1335,6 +1343,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
5 changes: 5 additions & 0 deletions identity/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ type CredentialsType string
// Please make sure to add all of these values to the test that ensures they are created during migration
const (
CredentialsTypePassword CredentialsType = "password"
CredentialsTypeOID2 CredentialsType = "oid2"
CredentialsTypeOIDC CredentialsType = "oidc"
CredentialsTypeTOTP CredentialsType = "totp"
CredentialsTypeLookup CredentialsType = "lookup_secret"
Expand All @@ -96,6 +97,8 @@ func (c CredentialsType) ToUiNodeGroup() node.UiNodeGroup {
switch c {
case CredentialsTypePassword:
return node.PasswordGroup
case CredentialsTypeOID2:
return node.OpenID2Group
case CredentialsTypeOIDC:
return node.OpenIDConnectGroup
case CredentialsTypeTOTP:
Expand All @@ -113,6 +116,7 @@ func (c CredentialsType) ToUiNodeGroup() node.UiNodeGroup {

var AllCredentialTypes = []CredentialsType{
CredentialsTypePassword,
CredentialsTypeOID2,
CredentialsTypeOIDC,
CredentialsTypeTOTP,
CredentialsTypeLookup,
Expand All @@ -131,6 +135,7 @@ const (
func ParseCredentialsType(in string) (CredentialsType, bool) {
for _, t := range []CredentialsType{
CredentialsTypePassword,
CredentialsTypeOID2,
CredentialsTypeOIDC,
CredentialsTypeTOTP,
CredentialsTypeLookup,
Expand Down
62 changes: 62 additions & 0 deletions identity/credentials_oid2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package identity

import (
"bytes"
"encoding/json"
"fmt"

"github.com/pkg/errors"

"github.com/ory/kratos/x"
)

// CredentialsOid2 contains the configuration for credentials of the type oidc.
//
// swagger:model identityCredentialsOidc
type CredentialsOid2 struct {
Providers []CredentialsOid2Provider `json:"providers"`
}

// CredentialsOid2 Provider contains a specific OpenID 2.0 credential for a particular connection (e.g. Steam).
//
// swagger:model identityCredentialsOid2Provider
type CredentialsOid2Provider struct {
ClaimedId string `json:"claimed_id"`
Provider string `json:"provider"`
}

// NewCredentialsOid2 creates a new Open ID 2.0 credential.
func NewCredentialsOid2(claimedId, provider string) (*Credentials, error) {
if provider == "" {
return nil, errors.New("received empty provider in oid2 credentials")
}

if claimedId == "" {
return nil, errors.New("received empty claimed ID in oid2 credentials")
}

var b bytes.Buffer
if err := json.NewEncoder(&b).Encode(CredentialsOid2{
Providers: []CredentialsOid2Provider{
{
ClaimedId: claimedId,
Provider: provider,
}},
}); err != nil {
return nil, errors.WithStack(x.PseudoPanic.
WithDebugf("Unable to encode password options to JSON: %s", err))
}

return &Credentials{
Type: CredentialsTypeOID2,
Identifiers: []string{Oid2UniqueID(provider, claimedId)},
Config: b.Bytes(),
}, nil
}

func Oid2UniqueID(provider, subject string) string {
return fmt.Sprintf("%s:%s", provider, subject)
}
2 changes: 1 addition & 1 deletion identity/credentials_oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func NewCredentialsOIDC(idToken, accessToken, refreshToken, provider, subject, o
}

if subject == "" {
return nil, errors.New("received empty provider in oidc credentials")
return nil, errors.New("received empty subject in oidc credentials")
}

var b bytes.Buffer
Expand Down
20 changes: 20 additions & 0 deletions selfservice/strategy/oid2/.schema/link.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$id": "https://schemas.ory.sh/kratos/selfservice/strategy/password/login.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"csrf_token": {
"type": "string"
},
"provider": {
"type": "string",
"minLength": 1
},
"traits": {
"description": "DO NOT DELETE THIS FIELD. This field will be overwritten in login.go's and registration.go's decoder() method. Do not add anything to this field as it has no effect."
},
"method": {
"type": "string"
}
}
}
21 changes: 21 additions & 0 deletions selfservice/strategy/oid2/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package oid2

import (
"context"

"github.com/ory/x/urlx"
"net/url"
"strings"
)

type Provider interface {
Config() *Configuration
GetRedirectUrl(ctx context.Context) string
}

func (providerConfig Configuration) Redir(public *url.URL) string {
return urlx.AppendPaths(public, strings.Replace(RouteCallback, ":provider", providerConfig.ID, 1)).String()
}
51 changes: 51 additions & 0 deletions selfservice/strategy/oid2/provider_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright © 2024 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package oid2

import (
"github.com/ory/herodot"
"github.com/pkg/errors"
"golang.org/x/exp/maps"
)

type Configuration struct {
// ID is the provider's ID
ID string `json:"id"`

// Provider is either "generic" for a generic OpenID 2.0 Provider or one of:
// - generic
// - steam
Provider string `json:"provider"`

// Label represents an optional label which can be used in the UI generation.
Label string `json:"label"`

// DiscoveryUrl is the URL of the Open ID 2.0 discovery document, typically something like:
// https://example.org/openid. Should only be used and when `provider` is set to `generic`.
DiscoveryUrl string `json:"discovery_url"`
}

type ConfigurationCollection struct {
BaseRedirectURI string `json:"base_redirect_uri"`
Providers []Configuration `json:"providers"`
}

var supportedProviders = map[string]func(config *Configuration, reg Dependencies) Provider{
"generic": NewProviderGenericOid2,
"steam": NewProviderSteam,
}

func (c ConfigurationCollection) Provider(id string, reg Dependencies) (Provider, error) {
for k := range c.Providers {
p := c.Providers[k]
if p.ID == id {
if f, ok := supportedProviders[p.Provider]; ok {
return f(&p, reg), nil
}

return nil, errors.Errorf("provider type %s is not supported, supported are: %v", p.Provider, maps.Keys(supportedProviders))
}
}
return nil, errors.WithStack(herodot.ErrNotFound.WithReasonf(`OpenID 2.0 Provider "%s" is unknown or has not been configured`, id))
}
Loading

0 comments on commit fd0e69a

Please sign in to comment.