Files
upstream-ory-kratos/selfservice/strategy/oidc/provider.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

166 lines
6.2 KiB
Go

// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package oidc
import (
"context"
"encoding/json"
"net/http"
"net/url"
"strings"
"github.com/dghubble/oauth1"
"github.com/pkg/errors"
"golang.org/x/oauth2"
"github.com/ory/herodot"
"github.com/ory/kratos/selfservice/strategy/oidc/oidcerr"
"github.com/ory/kratos/x"
)
type (
Provider interface {
Config() *Configuration
}
OAuth2Provider interface {
Provider
AuthCodeURLOptions(r ider) []oauth2.AuthCodeOption
OAuth2(ctx context.Context) (*oauth2.Config, error)
Claims(ctx context.Context, exchange *oauth2.Token, query url.Values) (*Claims, error)
}
OAuth1Provider interface {
Provider
OAuth1(ctx context.Context) *oauth1.Config
AuthURL(ctx context.Context, state string) (string, error)
Claims(ctx context.Context, token *oauth1.Token) (*Claims, error)
ExchangeToken(ctx context.Context, req *http.Request) (*oauth1.Token, error)
}
)
type OAuth2TokenExchanger interface {
Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error)
}
type IDTokenVerifier interface {
Verify(ctx context.Context, rawIDToken string) (*Claims, error)
}
type NonceValidationSkipper interface {
CanSkipNonce(*Claims) bool
}
type Claims struct {
Issuer string `json:"iss,omitempty"`
Subject string `json:"sub,omitempty"`
Object string `json:"oid,omitempty"`
Name string `json:"name,omitempty"`
GivenName string `json:"given_name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
LastName string `json:"last_name,omitempty"`
MiddleName string `json:"middle_name,omitempty"`
Nickname string `json:"nickname,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`
Profile string `json:"profile,omitempty"`
Picture string `json:"picture,omitempty"`
Website string `json:"website,omitempty"`
Email string `json:"email,omitempty"`
// ConvertibleBoolean is used as Apple casually sends the email_verified field as a string.
EmailVerified x.ConvertibleBoolean `json:"email_verified,omitempty"`
Gender string `json:"gender,omitempty"`
Birthdate string `json:"birthdate,omitempty"`
Zoneinfo string `json:"zoneinfo,omitempty"`
Locale Locale `json:"locale,omitempty"`
PhoneNumber string `json:"phone_number,omitempty"`
PhoneNumberVerified bool `json:"phone_number_verified,omitempty"`
UpdatedAt int64 `json:"updated_at,omitempty"`
HD string `json:"hd,omitempty"`
Team string `json:"team,omitempty"`
Nonce string `json:"nonce,omitempty"`
NonceSupported bool `json:"nonce_supported,omitempty"`
// ACR is the Authentication Context Class Reference reported by the
// upstream OIDC provider. See OpenID Connect Core 1.0, Section 2.
ACR string `json:"acr,omitempty"`
// AMR is the list of Authentication Methods References reported by the
// upstream OIDC provider. See OpenID Connect Core 1.0, Section 2 and
// RFC 8176.
AMR []string `json:"amr,omitempty"`
RawClaims map[string]any `json:"raw_claims,omitempty"`
}
type Locale string
func (l *Locale) UnmarshalJSON(data []byte) error {
var linkedInLocale struct {
Language string `json:"language"`
Country string `json:"country"`
}
if err := json.Unmarshal(data, &linkedInLocale); err == nil {
switch {
case linkedInLocale.Language == "":
*l = Locale(linkedInLocale.Country)
case linkedInLocale.Country == "":
*l = Locale(linkedInLocale.Language)
default:
*l = Locale(strings.Join([]string{linkedInLocale.Language, linkedInLocale.Country}, "-"))
}
return nil
}
return json.Unmarshal(data, (*string)(l))
}
// Validate checks if the claims are valid.
func (c *Claims) Validate() error {
if c.Subject == "" {
return oidcerr.Wrap(oidcerr.StepClaimsDecode, errors.WithStack(herodot.ErrUpstreamError().WithReasonf("provider did not return a subject")))
}
if c.Issuer == "" {
return oidcerr.Wrap(oidcerr.StepClaimsDecode, errors.WithStack(herodot.ErrUpstreamError().WithReasonf("issuer not set in claims")))
}
return nil
}
// UpstreamParameters returns a list of oauth2.AuthCodeOption based on the upstream parameters.
//
// Only allowed parameters are returned and the rest is ignored.
// Allowed parameters are also defined in the `oidc/.schema/link.schema.json` file, however,
// this function also validates the parameters to prevent any potential security issues.
//
// Allowed parameters are:
// - `hd` (string): The `hd` parameter limits the login/registration process to a Google Organization, e.g. `mycollege.edu`.
// - `prompt` (string): The `prompt` specifies whether the Authorization Server prompts the End-User for reauthentication and consent, e.g. `select_account`.
// - `auth_type` (string): The `auth_type` parameter specifies the requested authentication features (as a comma-separated list), e.g. `reauthenticate`.
// - `acr_values` (string): The `acr_values` specifies the Authentication Context Class Reference values for the authorization request.
//
// `login_hint` is intentionally not forwarded. A caller-supplied value cannot be
// trusted, and on IdPs that act on it (Google, Microsoft, Auth0, …) it can steer
// account, tenant, or OAuth client selection and enable a client-swap attack
// (HackerOne #3239672, ory-corp/cloud#8955).
func UpstreamParameters(upstreamParameters map[string]string) []oauth2.AuthCodeOption {
// validation of upstream parameters are already handled in the `oidc/.schema/link.schema.json` and `oidc/.schema/settings.schema.json` file.
// `upstreamParameters` will always only contain allowed parameters based on the configuration.
// we double-check the parameters here to prevent any potential security issues.
allowedParameters := map[string]struct{}{
"hd": {},
"prompt": {},
"auth_type": {},
"acr_values": {},
}
var params []oauth2.AuthCodeOption
for up, v := range upstreamParameters {
if _, ok := allowedParameters[up]; !ok {
continue
}
params = append(params, oauth2.SetAuthURLParam(up, v))
}
return params
}