Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: jackson provider #4242

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions driver/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ const (
ViperKeyIgnoreNetworkErrors = "selfservice.methods.password.config.ignore_network_errors"
ViperKeyTOTPIssuer = "selfservice.methods.totp.config.issuer"
ViperKeyOIDCBaseRedirectURL = "selfservice.methods.oidc.config.base_redirect_uri"
ViperKeySAMLBaseRedirectURL = "selfservice.methods.saml.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 @@ -616,6 +617,10 @@ func (p *Config) OIDCRedirectURIBase(ctx context.Context) *url.URL {
return p.GetProvider(ctx).URIF(ViperKeyOIDCBaseRedirectURL, p.SelfPublicURL(ctx))
}

func (p *Config) SAMLRedirectURIBase(ctx context.Context) *url.URL {
return p.GetProvider(ctx).URIF(ViperKeySAMLBaseRedirectURL, 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
86 changes: 86 additions & 0 deletions embedx/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,50 @@
}
]
},
"selfServiceSAMLProvider": {
"type": "object",
"properties": {
"id": {
"type": "string",
"examples": ["be2b30", "my-saml-provider"]
},
"label": {
"title": "Label",
"description": "Optional string which will be used when generating labels for UI buttons.",
"type": "string"
},
"mapper_url": {
"title": "Jsonnet Mapper URL",
"description": "The URL where the jsonnet source is located for mapping the provider's data to Ory Kratos data.",
"type": "string",
"format": "uri",
"examples": [
"file://path/to/oidc.jsonnet",
"https://foo.bar.com/path/to/oidc.jsonnet",
"base64://bG9jYWwgc3ViamVjdCA9I..."
]
},
"raw_idp_metadata_xml": {
"title": "Raw IDP Metadata XML",
"description": "The raw IDP metadata XML for the SAML provider.",
"type": "string",
"format": "uri",
"examples": [
"file://path/to/metadata.xml",
"https://foo.bar.com/path/to/metadata.xml",
"base64://bG9jYWwgc3ViamVjdCA9I..."
]
},
"organization_id": {
"title": "Organization ID",
"description": "The ID of the organization that this provider belongs to. Only effective in the Ory Network.",
"type": "string",
"examples": ["12345678-1234-1234-1234-123456789012"]
}
},
"additionalProperties": true,
"required": ["id", "raw_idp_metadata_xml", "mapper_url"]
},
"selfServiceHooks": {
"type": "array",
"items": {
Expand Down Expand Up @@ -867,6 +911,9 @@
"oidc": {
"$ref": "#/definitions/selfServiceAfterSettingsAuthMethod"
},
"saml": {
"$ref": "#/definitions/selfServiceAfterSettingsAuthMethod"
},
"webauthn": {
"$ref": "#/definitions/selfServiceAfterSettingsAuthMethod"
},
Expand Down Expand Up @@ -912,6 +959,9 @@
"oidc": {
"$ref": "#/definitions/selfServiceAfterOIDCLoginMethod"
},
"saml": {
"$ref": "#/definitions/selfServiceAfterOIDCLoginMethod"
},
"code": {
"$ref": "#/definitions/selfServiceAfterDefaultLoginMethod"
},
Expand Down Expand Up @@ -1005,6 +1055,9 @@
"oidc": {
"$ref": "#/definitions/selfServiceAfterRegistrationMethod"
},
"saml": {
"$ref": "#/definitions/selfServiceAfterRegistrationMethod"
},
"code": {
"$ref": "#/definitions/selfServiceAfterRegistrationMethod"
},
Expand Down Expand Up @@ -1962,6 +2015,39 @@
}
}
}
},
"saml": {
"type": "object",
"title": "Specify SAML configuration",
"additionalProperties": false,
"properties": {
"enabled": {
"type": "boolean",
"title": "Enables the SAML method",
"default": false
},
"config": {
"type": "object",
"additionalProperties": false,
"properties": {
"base_redirect_uri": {
"type": "string",
"title": "Base URL for SAML Redirect URIs",
"description": "Can be used to modify the base URL for SAML Redirect URLs. If unset, the Public Base URL will be used.",
"format": "uri",
"examples": ["https://auth.myexample.org/"]
},
"providers": {
"title": "SAML Providers",
"description": "A list and configuration of SAML providers Ory Kratos should integrate with.",
"type": "array",
"items": {
"$ref": "#/definitions/selfServiceSAMLProvider"
}
}
}
}
}
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions embedx/embedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ package embedx

import (
"bytes"
_ "embed"
"io"

"github.com/pkg/errors"

"github.com/ory/x/otelx"

"github.com/tidwall/gjson"

_ "embed"
"github.com/ory/x/otelx"
)

//go:embed config.schema.json
Expand Down
10 changes: 5 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ require (
go.opentelemetry.io/otel v1.32.0
go.opentelemetry.io/otel/sdk v1.32.0
go.opentelemetry.io/otel/trace v1.32.0
golang.org/x/crypto v0.29.0
golang.org/x/crypto v0.31.0
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
golang.org/x/net v0.31.0
golang.org/x/oauth2 v0.24.0
golang.org/x/sync v0.9.0
golang.org/x/text v0.20.0
golang.org/x/sync v0.10.0
golang.org/x/text v0.21.0
google.golang.org/grpc v1.67.1
)

Expand All @@ -118,7 +118,7 @@ require (
github.com/dgraph-io/ristretto/v2 v2.0.0 // indirect
github.com/jackc/pgx/v5 v5.6.0 // indirect
github.com/rjeczalik/notify v0.9.3 // indirect
golang.org/x/term v0.26.0 // indirect
golang.org/x/term v0.27.0 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
mvdan.cc/sh/v3 v3.6.0 // indirect
)
Expand Down Expand Up @@ -313,7 +313,7 @@ require (
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/tools v0.23.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
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 @@ -983,6 +985,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -1049,6 +1053,8 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand All @@ -1062,6 +1068,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
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 All @@ -1076,6 +1084,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
Expand Down
4 changes: 3 additions & 1 deletion identity/credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const (
CredentialsTypeCodeAuth CredentialsType = "code"
CredentialsTypePasskey CredentialsType = "passkey"
CredentialsTypeProfile CredentialsType = "profile"
CredentialsTypeSAML CredentialsType = "saml"
)

func (c CredentialsType) String() string {
Expand All @@ -99,7 +100,7 @@ func (c CredentialsType) ToUiNodeGroup() node.UiNodeGroup {
switch c {
case CredentialsTypePassword:
return node.PasswordGroup
case CredentialsTypeOIDC:
case CredentialsTypeOIDC, CredentialsTypeSAML:
return node.OpenIDConnectGroup
case CredentialsTypeTOTP:
return node.TOTPGroup
Expand Down Expand Up @@ -138,6 +139,7 @@ func ParseCredentialsType(in string) (CredentialsType, bool) {
for _, t := range []CredentialsType{
CredentialsTypePassword,
CredentialsTypeOIDC,
CredentialsTypeSAML,
CredentialsTypeTOTP,
CredentialsTypeLookup,
CredentialsTypeWebAuthn,
Expand Down
1 change: 1 addition & 0 deletions internal/client-go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
2 changes: 1 addition & 1 deletion persistence/sql/identity/persister_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -1337,7 +1337,7 @@ func FindIdentityCredentialsTypeByName(con *pop.Connection, ct identity.Credenti
}

if !found {
return uuid.Nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("The SQL adapter failed to return the appropriate credentials_type for nane %s. This is a bug in the code.", ct))
return uuid.Nil, errors.WithStack(herodot.ErrInternalServerError.WithReasonf("The SQL adapter failed to return the appropriate credentials_type for name %q. This is a bug in the code.", ct))
}

return result, nil
Expand Down
2 changes: 1 addition & 1 deletion selfservice/flow/login/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@
// We only apply the filter on AAL1, because the OIDC strategy can only satsify
// AAL1.
strategyFilters = []StrategyFilter{func(s Strategy) bool {
return s.ID() == identity.CredentialsTypeOIDC
return s.ID() == identity.CredentialsTypeOIDC || s.ID() == identity.CredentialsTypeSAML

Check warning on line 236 in selfservice/flow/login/handler.go

View check run for this annotation

Codecov / codecov/patch

selfservice/flow/login/handler.go#L236

Added line #L236 was not covered by tests
}}
}
}
Expand Down
4 changes: 3 additions & 1 deletion selfservice/flow/registration/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@
h.d.Logger().WithError(err).Warnf("ignoring invalid UUID %q in query parameter `organization`", rawOrg)
} else {
f.OrganizationID = uuid.NullUUID{UUID: orgID, Valid: true}
strategyFilters = []StrategyFilter{func(s Strategy) bool { return s.ID() == identity.CredentialsTypeOIDC }}
strategyFilters = []StrategyFilter{func(s Strategy) bool {
return s.ID() == identity.CredentialsTypeOIDC || s.ID() == identity.CredentialsTypeSAML
}}

Check warning on line 146 in selfservice/flow/registration/handler.go

View check run for this annotation

Codecov / codecov/patch

selfservice/flow/registration/handler.go#L144-L146

Added lines #L144 - L146 were not covered by tests
}
}
for _, s := range h.d.RegistrationStrategies(r.Context(), strategyFilters...) {
Expand Down
2 changes: 1 addition & 1 deletion selfservice/flow/registration/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ func TestOIDCStrategyOrder(t *testing.T) {

// reorder the strategies
reg.WithSelfserviceStrategies(t, []any{
oidc.NewStrategy(reg),
oidc.NewStrategy(reg, oidc.ForCredentialType(identity.CredentialsTypeOIDC)),
password.NewStrategy(reg),
})

Expand Down
1 change: 1 addition & 0 deletions selfservice/strategy/oidc/provider_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ var supportedProviders = map[string]func(config *Configuration, reg Dependencies
"patreon": NewProviderPatreon,
"lark": NewProviderLark,
"x": NewProviderX,
"jackson": NewProviderJackson,
}

func (c ConfigurationCollection) Provider(id string, reg Dependencies) (Provider, error) {
Expand Down
57 changes: 57 additions & 0 deletions selfservice/strategy/oidc/provider_jackson.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

package oidc

import (
"context"
"strings"

"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"

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

type ProviderJackson struct {
*ProviderGenericOIDC
}

func NewProviderJackson(
config *Configuration,
reg Dependencies,
) Provider {
return &ProviderJackson{
ProviderGenericOIDC: &ProviderGenericOIDC{
config: config,
reg: reg,
},
}
}

func (j *ProviderJackson) setProvider(ctx context.Context) {
if j.ProviderGenericOIDC.p == nil {
internalHost := strings.TrimSuffix(j.config.TokenURL, "/api/oauth/token")
config := oidc.ProviderConfig{
IssuerURL: j.config.IssuerURL,
AuthURL: j.config.AuthURL,
TokenURL: j.config.TokenURL,
DeviceAuthURL: "",
UserInfoURL: internalHost + "/api/oauth/userinfo",
JWKSURL: internalHost + "/oauth/jwks",
Algorithms: []string{"RS256"},
}
j.ProviderGenericOIDC.p = config.NewProvider(j.withHTTPClientContext(ctx))
}
}

func (j *ProviderJackson) OAuth2(ctx context.Context) (*oauth2.Config, error) {
j.setProvider(ctx)
endpoint := j.ProviderGenericOIDC.p.Endpoint()
config := j.oauth2ConfigFromEndpoint(ctx, endpoint)
config.RedirectURL = urlx.AppendPaths(
j.reg.Config().SAMLRedirectURIBase(ctx),
"/self-service/methods/saml/callback/"+j.config.ID).String()

return config, nil
}
Loading
Loading