Files
upstream-ory-kratos/hydra/hydra.go
upstream-mirror c60cc7ca75
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:19:12 +05:00

173 lines
5.2 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package hydra
import (
"context"
"net/http"
"time"
"github.com/ory/x/httpx"
"github.com/ory/x/sqlxx"
"github.com/pkg/errors"
"github.com/ory/herodot"
hydraclientgo "github.com/ory/hydra-client-go/v2"
"github.com/ory/kratos/driver/config"
"github.com/ory/kratos/session"
)
type (
hydraDependencies interface {
config.Provider
httpx.ClientProvider
}
Provider interface {
Hydra() Hydra
}
AcceptLoginRequestParams struct {
LoginChallenge string
IdentityID string
SessionID string
AuthenticationMethods session.AuthenticationMethods
}
Hydra interface {
AcceptLoginRequest(ctx context.Context, params AcceptLoginRequestParams) (string, error)
GetLoginRequest(ctx context.Context, loginChallenge string) (*hydraclientgo.OAuth2LoginRequest, error)
}
DefaultHydra struct {
d hydraDependencies
}
)
func NewDefaultHydra(d hydraDependencies) *DefaultHydra {
return &DefaultHydra{
d: d,
}
}
func GetLoginChallengeID(conf *config.Config, r *http.Request) (sqlxx.NullString, error) {
if !r.URL.Query().Has("login_challenge") {
return "", nil
} else if conf.OAuth2ProviderURL(r.Context()) == nil {
return "", errors.WithStack(herodot.ErrMisconfiguration().WithReason("refusing to parse login_challenge query parameter because " + config.ViperKeyOAuth2ProviderURL + " is invalid or unset"))
}
loginChallenge := r.URL.Query().Get("login_challenge")
if loginChallenge == "" {
return "", errors.WithStack(herodot.ErrBadRequest().WithReason("the login_challenge parameter is present but empty"))
}
return sqlxx.NullString(loginChallenge), nil
}
func (h *DefaultHydra) getAdminURL(ctx context.Context) (string, error) {
u := h.d.Config().OAuth2ProviderURL(ctx)
if u == nil {
return "", errors.WithStack(herodot.ErrMisconfiguration().WithReason(config.ViperKeyOAuth2ProviderURL + " is not configured"))
}
return u.String(), nil
}
func (h *DefaultHydra) getAdminAPIClient(ctx context.Context) (hydraclientgo.OAuth2API, error) {
url, err := h.getAdminURL(ctx)
if err != nil {
return nil, err
}
configuration := hydraclientgo.NewConfiguration()
configuration.Servers = hydraclientgo.ServerConfigurations{{URL: url}}
client := h.d.HTTPClient(ctx).StandardClient()
if header := h.d.Config().OAuth2ProviderHeader(ctx); header != nil {
client.Transport = httpx.WrapTransportWithHeader(client.Transport, header)
}
configuration.HTTPClient = client
return hydraclientgo.NewAPIClient(configuration).OAuth2API, nil
}
func (h *DefaultHydra) AcceptLoginRequest(ctx context.Context, params AcceptLoginRequestParams) (string, error) {
remember := h.d.Config().SessionPersistentCookie(ctx)
rememberFor := int64(h.d.Config().SessionLifespan(ctx) / time.Second)
alr := hydraclientgo.NewAcceptOAuth2LoginRequest(params.IdentityID)
alr.IdentityProviderSessionId = &params.SessionID
alr.Remember = &remember
alr.RememberFor = &rememberFor
alr.Amr = []string{}
for _, r := range params.AuthenticationMethods {
alr.Amr = append(alr.Amr, string(r.Method))
}
aa, err := h.getAdminAPIClient(ctx)
if err != nil {
return "", err
}
resp, r, err := aa.AcceptOAuth2LoginRequest(ctx).LoginChallenge(params.LoginChallenge).AcceptOAuth2LoginRequest(*alr).Execute()
if err != nil {
innerErr := herodot.ErrInternalServerError().WithWrap(err).WithReasonf("Unable to accept OAuth 2.0 Login Challenge.")
if r != nil {
innerErr = innerErr.
WithDetail("status_code", r.StatusCode).
WithDebug(err.Error())
}
if openApiErr := new(hydraclientgo.GenericOpenAPIError); errors.As(err, &openApiErr) {
switch oauth2Err := openApiErr.Model().(type) {
case hydraclientgo.ErrorOAuth2:
innerErr = innerErr.WithDetail("oauth2_error_hint", oauth2Err.GetErrorHint())
case *hydraclientgo.ErrorOAuth2:
innerErr = innerErr.WithDetail("oauth2_error_hint", oauth2Err.GetErrorHint())
}
}
return "", errors.WithStack(innerErr)
}
return resp.RedirectTo, nil
}
func (h *DefaultHydra) GetLoginRequest(ctx context.Context, loginChallenge string) (*hydraclientgo.OAuth2LoginRequest, error) {
if loginChallenge == "" {
return nil, errors.WithStack(herodot.ErrBadRequest().WithReason("invalid login_challenge"))
}
aa, err := h.getAdminAPIClient(ctx)
if err != nil {
return nil, err
}
hlr, r, err := aa.GetOAuth2LoginRequest(ctx).LoginChallenge(loginChallenge).Execute()
if err != nil {
var innerErr *herodot.DefaultError
if r == nil || r.StatusCode >= 500 {
innerErr = herodot.ErrInternalServerError()
} else {
innerErr = herodot.ErrBadRequest()
}
innerErr = innerErr.WithReasonf("Unable to get OAuth 2.0 Login Challenge.")
if r != nil {
innerErr = innerErr.
WithDetail("status_code", r.StatusCode).
WithDebug(err.Error())
}
if openApiErr := new(hydraclientgo.GenericOpenAPIError); errors.As(err, &openApiErr) {
switch oauth2Err := openApiErr.Model().(type) {
case hydraclientgo.ErrorOAuth2:
innerErr = innerErr.WithDetail("oauth2_error_hint", oauth2Err.GetErrorHint())
case *hydraclientgo.ErrorOAuth2:
innerErr = innerErr.WithDetail("oauth2_error_hint", oauth2Err.GetErrorHint())
}
}
return nil, errors.WithStack(innerErr)
}
return hlr, nil
}