Skip to content

Commit

Permalink
feat: OAuth2 integration (#2804)
Browse files Browse the repository at this point in the history
  • Loading branch information
aeneasr committed Oct 26, 2022
1 parent 816b029 commit 7c6eb2a
Show file tree
Hide file tree
Showing 118 changed files with 9,503 additions and 653 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cve-scan.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ jobs:
acs-report-enable: true
- name: Anchore upload scan SARIF report
if: always()
uses: github/codeql-action/upload-sarif@v1
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: ${{ steps.grype-scan.outputs.sarif }}
- name: Trivy Scanner
Expand Down
43 changes: 43 additions & 0 deletions .schema/openapi/patches/nulls.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
- op: replace
path: "#/components/schemas/NullUUID"
value:
type: string
format: uuid4
nullable: true
- op: replace
path: "#/components/schemas/NullTime"
value:
format: date-time
type: string
nullable: true
- op: replace
path: "#/components/schemas/Time"
value:
format: date-time
type: string
- op: replace
path: "#/components/schemas/NullString"
value:
type: string
nullable: true
- op: replace
path: "#/components/schemas/NullBool"
value:
type: boolean
nullable: true
- op: replace
path: "#/components/schemas/NullInt"
value:
type: integer
nullable: true
- op: replace
path: "#/components/schemas/nullInt64"
value:
type: integer
nullable: true
- op: replace
path: "#/components/schemas/nullDuration"
value:
type: string
nullable: true
pattern: ^[0-9]+(ns|us|ms|s|m|h)$
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ sdk: .bin/swagger .bin/ory node_modules
-p file://.schema/openapi/patches/identity.yaml \
-p file://.schema/openapi/patches/courier.yaml \
-p file://.schema/openapi/patches/generic_error.yaml \
-p file://.schema/openapi/patches/nulls.yaml \
-p file://.schema/openapi/patches/common.yaml \
spec/swagger.json spec/api.json

Expand Down
35 changes: 34 additions & 1 deletion driver/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,8 @@ const (
ViperKeyWebAuthnRPOrigin = "selfservice.methods.webauthn.config.rp.origin"
ViperKeyWebAuthnRPIcon = "selfservice.methods.webauthn.config.rp.issuer"
ViperKeyWebAuthnPasswordless = "selfservice.methods.webauthn.config.passwordless"
ViperKeyOAuth2ProviderURL = "oauth2_provider.url"
ViperKeyOAuth2ProviderHeader = "oauth2_provider.headers"
ViperKeyClientHTTPNoPrivateIPRanges = "clients.http.disallow_private_ip_ranges"
ViperKeyClientHTTPPrivateIPExceptionURLs = "clients.http.private_ip_exception_urls"
ViperKeyVersion = "version"
Expand Down Expand Up @@ -848,6 +850,37 @@ func (p *Config) CourierSMTPURL(ctx context.Context) *url.URL {
return p.ParseURIOrFail(ctx, ViperKeyCourierSMTPURL)
}

func (p *Config) OAuth2ProviderHeader(ctx context.Context) http.Header {
hh := map[string]string{}
if err := p.GetProvider(ctx).Unmarshal(ViperKeyOAuth2ProviderHeader, &hh); err != nil {
p.l.WithError(errors.WithStack(err)).
Errorf("Configuration value from key %s could not be decoded.", ViperKeyOAuth2ProviderHeader)
return nil
}

h := make(http.Header)
for k, v := range hh {
h.Set(k, v)
}

return h
}

func (p *Config) OAuth2ProviderURL(ctx context.Context) *url.URL {
k := ViperKeyOAuth2ProviderURL
v := p.GetProvider(ctx).String(k)
if v == "" {
return nil
}
parsed, err := p.ParseAbsoluteOrRelativeURI(v)
if err != nil {
p.l.WithError(errors.WithStack(err)).
Errorf("Configuration value from key %s is not a valid URL: %s", k, v)
return nil
}
return parsed
}

func (p *Config) SelfServiceFlowLoginUI(ctx context.Context) *url.URL {
return p.ParseAbsoluteOrRelativeURIOrFail(ctx, ViperKeySelfServiceLoginUI)
}
Expand All @@ -868,7 +901,7 @@ func (p *Config) SelfServiceFlowRecoveryUI(ctx context.Context) *url.URL {
return p.ParseAbsoluteOrRelativeURIOrFail(ctx, ViperKeySelfServiceRecoveryUI)
}

// SessionLifespan returns nil when the value is not set.
// SessionLifespan returns time.Hour*24 when the value is not set.
func (p *Config) SessionLifespan(ctx context.Context) time.Duration {
return p.GetProvider(ctx).DurationF(ViperKeySessionLifespan, time.Hour*24)
}
Expand Down
17 changes: 17 additions & 0 deletions driver/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,23 @@ func TestCourierMessageTTL(t *testing.T) {
})
}

func TestOAuth2Provider(t *testing.T) {
ctx := context.Background()

t.Run("case=configs set", func(t *testing.T) {
conf, _ := config.New(ctx, logrusx.New("", ""), os.Stderr,
configx.WithConfigFiles("stub/.kratos.oauth2_provider.yaml"), configx.SkipValidation())
assert.Equal(t, "https://oauth2_provider/", conf.OAuth2ProviderURL(ctx).String())
assert.Equal(t, http.Header{"Authorization": {"Basic"}}, conf.OAuth2ProviderHeader(ctx))
})

t.Run("case=defaults", func(t *testing.T) {
conf, _ := config.New(ctx, logrusx.New("", ""), os.Stderr, configx.SkipValidation())
assert.Empty(t, conf.OAuth2ProviderURL(ctx))
assert.Empty(t, conf.OAuth2ProviderHeader(ctx))
})
}

func TestCourierTemplatesConfig(t *testing.T) {
ctx := context.Background()

Expand Down
4 changes: 4 additions & 0 deletions driver/config/stub/.kratos.oauth2_provider.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
oauth2_provider:
url: https://oauth2_provider/
headers:
Authorization: Basic
15 changes: 15 additions & 0 deletions driver/registry_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

"github.com/ory/nosurf"

"github.com/ory/kratos/hydra"
"github.com/ory/kratos/selfservice/strategy/code"
"github.com/ory/kratos/selfservice/strategy/webauthn"

Expand Down Expand Up @@ -146,6 +147,8 @@ type RegistryDefault struct {

selfserviceStrategies []interface{}

hydra hydra.Hydra

buildVersion string
buildHash string
buildDate string
Expand Down Expand Up @@ -517,6 +520,18 @@ func (m *RegistryDefault) SessionManager() session.Manager {
return m.sessionManager
}

func (m *RegistryDefault) Hydra() hydra.Hydra {
if m.hydra == nil {
m.hydra = hydra.NewDefaultHydra(m)
}
return m.hydra
}

func (m *RegistryDefault) WithHydra(h hydra.Hydra) Registry {
m.hydra = h
return m
}

func (m *RegistryDefault) SelfServiceErrorManager() *errorx.Manager {
if m.errorManager == nil {
m.errorManager = errorx.NewManager(m)
Expand Down
58 changes: 48 additions & 10 deletions embedx/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,7 @@
},
"anyOf": [
{
"not":
{
"not": {
"properties": {
"response": {
"properties": {
Expand All @@ -241,10 +240,14 @@
]
}
},
"required": ["ignore"]
"required": [
"ignore"
]
}
},
"required": ["response"]
"required": [
"response"
]
}
},
{
Expand All @@ -255,7 +258,9 @@
]
}
},
"require": ["can_interrupt"]
"require": [
"can_interrupt"
]
}
],
"additionalProperties": false,
Expand Down Expand Up @@ -1223,7 +1228,7 @@
},
"use": {
"title": "Recovery Strategy",
"description":"The strategy to use for recovery requests",
"description": "The strategy to use for recovery requests",
"type": "string",
"enum": [
"link",
Expand Down Expand Up @@ -1545,7 +1550,7 @@
}
}
},
"database": {
"database": {
"type": "object",
"title": "Database related configuration",
"description": "Miscellaneous settings used in database related tasks (cleanup, etc.)",
Expand All @@ -1555,7 +1560,7 @@
"title": "Database cleanup settings",
"description": "Settings that controls how the database cleanup process is configured (delays, batch size, etc.)",
"properties": {
"batch_size" : {
"batch_size": {
"type": "integer",
"title": "Number of records to clean in one iteration",
"description": "Controls how many records should be purged from one table during database cleanup task",
Expand Down Expand Up @@ -1687,6 +1692,9 @@
"title": "SMTP Headers",
"description": "These headers will be passed in the SMTP conversation -- e.g. when using the AWS SES SMTP interface for cross-account sending.",
"type": "object",
"additionalProperties": {
"type": "string"
},
"examples": [
{
"X-SES-SOURCE-ARN": "arn:aws:ses:us-west-2:123456789012:identity/example.com",
Expand Down Expand Up @@ -1739,7 +1747,7 @@
"type": "string",
"description": "The HTTP method to use (GET, POST, etc)."
},
"header": {
"headers": {
"type": "object",
"description": "The HTTP headers that must be applied to request",
"additionalProperties": {
Expand Down Expand Up @@ -1788,6 +1796,36 @@
],
"additionalProperties": false
},
"oauth2_provider": {
"title": "OAuth2 Provider Configuration",
"type": "object",
"properties": {
"url": {
"title": "OAuth 2.0 Provider URL.",
"description": "If set, the login and registration flows will handle the Ory OAuth 2.0 & OpenID `login_challenge` query parameter to serve as an OpenID Connect Provider. This URL should point to Ory Hydra when you are not running on the Ory Network and be left untouched otherwise.",
"type": "string",
"format": "uri",
"examples": [
"https://some-slug.projects.oryapis.com",
"https://domain-of-ory-hydra:4445"
]
},
"headers": {
"title": "HTTP Request Headers",
"description": "These headers will be passed in HTTP request to the OAuth2 Provider.",
"type": "object",
"additionalProperties": {
"type": "string"
},
"examples": [
{
"Authorization": "Bearer some-token"
}
]
}
},
"additionalProperties": false
},
"serve": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -2318,7 +2356,7 @@
},
"persistent": {
"title": "Make Session Cookie Persistent",
"description": "If set to true will persist the cookie in the end-user's browser using the `max-age` parameter which is set to the `session.lifespan` value. Persistent cookies are not deleted when the browser is closed (e.g. on reboot or alt+f4).",
"description": "If set to true will persist the cookie in the end-user's browser using the `max-age` parameter which is set to the `session.lifespan` value. Persistent cookies are not deleted when the browser is closed (e.g. on reboot or alt+f4). This option affects the Ory OAuth2 and OpenID Provider's remember feature as well.",
"type": "boolean",
"default": true
},
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,12 @@ require (
github.com/ory/go-convenience v0.1.0
github.com/ory/graceful v0.1.3
github.com/ory/herodot v0.9.13
github.com/ory/hydra-client-go v1.11.8
github.com/ory/jsonschema/v3 v3.0.7
github.com/ory/kratos-client-go v0.6.3-alpha.1
github.com/ory/mail/v3 v3.0.0
github.com/ory/nosurf v1.2.7
github.com/ory/x v0.0.480
github.com/ory/x v0.0.488
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.3.0
Expand Down
7 changes: 5 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,8 @@ github.com/ory/graceful v0.1.3 h1:FaeXcHZh168WzS+bqruqWEw/HgXWLdNv2nJ+fbhxbhc=
github.com/ory/graceful v0.1.3/go.mod h1:4zFz687IAF7oNHHiB586U4iL+/4aV09o/PYLE34t2bA=
github.com/ory/herodot v0.9.13 h1:cN/Z4eOkErl/9W7hDIDLb79IO/bfsH+8yscBjRpB4IU=
github.com/ory/herodot v0.9.13/go.mod h1:IWDs9kSvFQqw/cQ8zi5ksyYvITiUU4dI7glUrhZcJYo=
github.com/ory/hydra-client-go v1.11.8 h1:GwJjvH/DBcfYzoST4vUpi4pIRzDGH5oODKpIVuhwVyc=
github.com/ory/hydra-client-go v1.11.8/go.mod h1:4YuBuwUEC4yiyDrnKjGYc1tB3gUXan4ZiUYMjXJbfxA=
github.com/ory/jsonschema/v3 v3.0.7 h1:GQ9qfZDiJqs4l2d3p56dozCChvejQFZyLKGHYzDzOSo=
github.com/ory/jsonschema/v3 v3.0.7/go.mod h1:g8c8YOtN4TrR2wYeMdT02GDmzJDI0fEW2nI26BECafY=
github.com/ory/mail v2.3.1+incompatible/go.mod h1:87D9/1gB6ewElQoN0lXJ0ayfqcj3cW3qCTXh+5E9mfU=
Expand All @@ -1381,8 +1383,8 @@ github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2 h1:zm6sDvHy/U9XrGpi
github.com/ory/sessions v1.2.2-0.20220110165800-b09c17334dc2/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/ory/viper v1.7.5 h1:+xVdq7SU3e1vNaCsk/ixsfxE4zylk1TJUiJrY647jUE=
github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTaM=
github.com/ory/x v0.0.480 h1:IAflszUfmpy/bVnd8gxIgKuL9pL1oLjytxqCmAMC14o=
github.com/ory/x v0.0.480/go.mod h1:w2gwqgw3XqKTxW8wURVxUFI2NuDyIC2rGxvEsnBJqjs=
github.com/ory/x v0.0.488 h1:EQLqYLPwNfs9OW9GFrxDEO11oEmKpvveN1wuUfU8yOU=
github.com/ory/x v0.0.488/go.mod h1:dJ800rWC2/eNECWhXMyI9kSd7lO2LTOu6R8oS0lQZ38=
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
Expand Down Expand Up @@ -2011,6 +2013,7 @@ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210323180902-22b0adad7558/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
Expand Down
45 changes: 45 additions & 0 deletions hydra/fake.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package hydra

import (
"context"
"errors"

"github.com/gofrs/uuid"

hydraclientgo "github.com/ory/hydra-client-go"
"github.com/ory/kratos/session"
)

const (
FAKE_GET_LOGIN_REQUEST_RETURN_NIL_NIL = "b805f2d9-2f6d-4745-9d68-a17f48e25774"
FAKE_ACCEPT_REQUEST_FAIL = "2e98454e-031b-4870-9ad6-8517df1ce604"
FAKE_SUCCESS = "5ff59a39-ecc5-467e-bb10-26644c0700ee"
)

type FakeHydra struct{}

var _ Hydra = &FakeHydra{}

func NewFakeHydra() *FakeHydra {
return &FakeHydra{}
}

func (h *FakeHydra) AcceptLoginRequest(ctx context.Context, hlc uuid.UUID, sub string, amr session.AuthenticationMethods) (string, error) {
switch hlc.String() {
case FAKE_ACCEPT_REQUEST_FAIL:
return "", errors.New("failed to accept login request")
default:
panic("unknown fake login_challenge " + hlc.String())
}
}

func (h *FakeHydra) GetLoginRequest(ctx context.Context, hlc uuid.NullUUID) (*hydraclientgo.LoginRequest, error) {
switch hlc.UUID.String() {
case FAKE_ACCEPT_REQUEST_FAIL:
return &hydraclientgo.LoginRequest{}, nil
case FAKE_SUCCESS:
return &hydraclientgo.LoginRequest{}, nil
default:
panic("unknown fake login_challenge " + hlc.UUID.String())
}
}
Loading

0 comments on commit 7c6eb2a

Please sign in to comment.