client/handler.go
// Copyright © 2022 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package client
import (
"context"
"crypto/subtle"
"encoding/json"
"io"
"net/http"
"strings"
"time"
"github.com/julienschmidt/httprouter"
"github.com/pkg/errors"
"github.com/ory/fosite"
"github.com/ory/herodot"
"github.com/ory/hydra/v2/x"
"github.com/ory/x/errorsx"
"github.com/ory/x/httprouterx"
"github.com/ory/x/jsonx"
"github.com/ory/x/openapix"
"github.com/ory/x/pagination/tokenpagination"
"github.com/ory/x/urlx"
"github.com/ory/x/uuidx"
)
type Handler struct {
r InternalRegistry
}
const (
ClientsHandlerPath = "/clients"
DynClientsHandlerPath = "/oauth2/register"
)
func NewHandler(r InternalRegistry) *Handler {
return &Handler{
r: r,
}
}
func (h *Handler) SetRoutes(admin *httprouterx.RouterAdmin, public *httprouterx.RouterPublic) {
admin.GET(ClientsHandlerPath, h.listOAuth2Clients)
admin.POST(ClientsHandlerPath, h.createOAuth2Client)
admin.GET(ClientsHandlerPath+"/:id", h.Get)
admin.PUT(ClientsHandlerPath+"/:id", h.setOAuth2Client)
admin.PATCH(ClientsHandlerPath+"/:id", h.patchOAuth2Client)
admin.DELETE(ClientsHandlerPath+"/:id", h.deleteOAuth2Client)
admin.PUT(ClientsHandlerPath+"/:id/lifespans", h.setOAuth2ClientLifespans)
public.POST(DynClientsHandlerPath, h.createOidcDynamicClient)
public.GET(DynClientsHandlerPath+"/:id", h.getOidcDynamicClient)
public.PUT(DynClientsHandlerPath+"/:id", h.setOidcDynamicClient)
public.DELETE(DynClientsHandlerPath+"/:id", h.deleteOidcDynamicClient)
}
// OAuth 2.0 Client Creation Parameters
//
// swagger:parameters createOAuth2Client
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type createOAuth2Client struct {
// OAuth 2.0 Client Request Body
//
// in: body
// required: true
Body Client
}
// swagger:route POST /admin/clients oAuth2 createOAuth2Client
//
// # Create OAuth 2.0 Client
//
// Create a new OAuth 2.0 client. If you pass `client_secret` the secret is used, otherwise a random secret
// is generated. The secret is echoed in the response. It is not possible to retrieve it later on.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 201: oAuth2Client
// 400: errorOAuth2BadRequest
// default: errorOAuth2Default
func (h *Handler) createOAuth2Client(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
c, err := h.CreateClient(r, h.r.ClientValidator().Validate, false)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
h.r.Writer().WriteCreated(w, r, "/admin"+ClientsHandlerPath+"/"+c.GetID(), &c)
}
// OpenID Connect Dynamic Client Registration Parameters
//
// swagger:parameters createOidcDynamicClient
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type createOidcDynamicClient struct {
// Dynamic Client Registration Request Body
//
// in: body
// required: true
Body Client
}
// swagger:route POST /oauth2/register oidc createOidcDynamicClient
//
// # Register OAuth2 Client using OpenID Dynamic Client Registration
//
// This endpoint behaves like the administrative counterpart (`createOAuth2Client`) but is capable of facing the
// public internet directly and can be used in self-service. It implements the OpenID Connect
// Dynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint
// is disabled by default. It can be enabled by an administrator.
//
// Please note that using this endpoint you are not able to choose the `client_secret` nor the `client_id` as those
// values will be server generated when specifying `token_endpoint_auth_method` as `client_secret_basic` or
// `client_secret_post`.
//
// The `client_secret` will be returned in the response and you will not be able to retrieve it later on.
// Write the secret down and keep it somewhere safe.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 201: oAuth2Client
// 400: errorOAuth2BadRequest
// default: errorOAuth2Default
func (h *Handler) createOidcDynamicClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if err := h.requireDynamicAuth(r); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
c, err := h.CreateClient(r, h.r.ClientValidator().ValidateDynamicRegistration, true)
if err != nil {
h.r.Writer().WriteError(w, r, errorsx.WithStack(err))
return
}
h.r.Writer().WriteCreated(w, r, "/admin"+ClientsHandlerPath+"/"+c.GetID(), &c)
}
func (h *Handler) CreateClient(r *http.Request, validator func(context.Context, *Client) error, isDynamic bool) (*Client, error) {
var c Client
if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
return nil, errorsx.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to decode the request body: %s", err))
}
if isDynamic {
if c.Secret != "" {
return nil, errorsx.WithStack(herodot.ErrBadRequest.WithReasonf("It is not allowed to choose your own OAuth2 Client secret."))
}
// We do not allow to set the client ID for dynamic clients.
c.ID = uuidx.NewV4().String()
}
if len(c.Secret) == 0 {
secretb, err := x.GenerateSecret(26)
if err != nil {
return nil, err
}
c.Secret = string(secretb)
}
if err := validator(r.Context(), &c); err != nil {
return nil, err
}
secret := c.Secret
c.CreatedAt = time.Now().UTC().Round(time.Second)
c.UpdatedAt = c.CreatedAt
token, signature, err := h.r.OAuth2HMACStrategy().GenerateAccessToken(r.Context(), nil)
if err != nil {
return nil, err
}
c.RegistrationAccessToken = token
c.RegistrationAccessTokenSignature = signature
c.RegistrationClientURI = urlx.AppendPaths(h.r.Config().PublicURL(r.Context()), DynClientsHandlerPath+"/"+c.GetID()).String()
if err := h.r.ClientManager().CreateClient(r.Context(), &c); err != nil {
return nil, err
}
c.Secret = ""
if !c.IsPublic() {
c.Secret = secret
}
return &c, nil
}
// Set OAuth 2.0 Client Parameters
//
// swagger:parameters setOAuth2Client
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type setOAuth2Client struct {
// OAuth 2.0 Client ID
//
// in: path
// required: true
ID string `json:"id"`
// OAuth 2.0 Client Request Body
//
// in: body
// required: true
Body Client
}
// swagger:route PUT /admin/clients/{id} oAuth2 setOAuth2Client
//
// # Set OAuth 2.0 Client
//
// Replaces an existing OAuth 2.0 Client with the payload you send. If you pass `client_secret` the secret is used,
// otherwise the existing secret is used.
//
// If set, the secret is echoed in the response. It is not possible to retrieve it later on.
//
// OAuth 2.0 Clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are
// generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 200: oAuth2Client
// 400: errorOAuth2BadRequest
// 404: errorOAuth2NotFound
// default: errorOAuth2Default
func (h *Handler) setOAuth2Client(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var c Client
if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
h.r.Writer().WriteError(w, r, errorsx.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to decode the request body: %s", err)))
return
}
c.ID = ps.ByName("id")
if err := h.updateClient(r.Context(), &c, h.r.ClientValidator().Validate); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
h.r.Writer().Write(w, r, &c)
}
func (h *Handler) updateClient(ctx context.Context, c *Client, validator func(context.Context, *Client) error) error {
var secret string
if len(c.Secret) > 0 {
secret = c.Secret
}
if err := validator(ctx, c); err != nil {
return err
}
c.UpdatedAt = time.Now().UTC().Round(time.Second)
if err := h.r.ClientManager().UpdateClient(ctx, c); err != nil {
return err
}
c.Secret = secret
return nil
}
// Set Dynamic Client Parameters
//
// swagger:parameters setOidcDynamicClient
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type setOidcDynamicClient struct {
// OAuth 2.0 Client ID
//
// in: path
// required: true
ID string `json:"id"`
// OAuth 2.0 Client Request Body
//
// in: body
// required: true
Body Client
}
// swagger:route PUT /oauth2/register/{id} oidc setOidcDynamicClient
//
// # Set OAuth2 Client using OpenID Dynamic Client Registration
//
// This endpoint behaves like the administrative counterpart (`setOAuth2Client`) but is capable of facing the
// public internet directly to be used by third parties. It implements the OpenID Connect
// Dynamic Client Registration Protocol.
//
// This feature is disabled per default. It can be enabled by a system administrator.
//
// If you pass `client_secret` the secret is used, otherwise the existing secret is used. If set, the secret is echoed in the response.
// It is not possible to retrieve it later on.
//
// To use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client
// uses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.
// If it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are
// generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Security:
// bearer:
//
// Schemes: http, https
//
// Responses:
// 200: oAuth2Client
// 404: errorOAuth2NotFound
// default: errorOAuth2Default
func (h *Handler) setOidcDynamicClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if err := h.requireDynamicAuth(r); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
client, err := h.ValidDynamicAuth(r, ps)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
var c Client
if err := json.NewDecoder(r.Body).Decode(&c); err != nil {
h.r.Writer().WriteError(w, r, errorsx.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to decode the request body. Is it valid JSON?").WithDebug(err.Error())))
return
}
if c.Secret != "" {
h.r.Writer().WriteError(w, r, errorsx.WithStack(herodot.ErrForbidden.WithReasonf("It is not allowed to choose your own OAuth2 Client secret.")))
return
}
// Regenerate the registration access token
token, signature, err := h.r.OAuth2HMACStrategy().GenerateAccessToken(r.Context(), nil)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
c.RegistrationAccessToken = token
c.RegistrationAccessTokenSignature = signature
c.ID = client.GetID()
if err := h.updateClient(r.Context(), &c, h.r.ClientValidator().ValidateDynamicRegistration); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
h.r.Writer().Write(w, r, &c)
}
// Patch OAuth 2.0 Client Parameters
//
// swagger:parameters patchOAuth2Client
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type patchOAuth2Client struct {
// The id of the OAuth 2.0 Client.
//
// in: path
// required: true
ID string `json:"id"`
// OAuth 2.0 Client JSON Patch Body
//
// in: body
// required: true
Body openapix.JSONPatchDocument
}
// swagger:route PATCH /admin/clients/{id} oAuth2 patchOAuth2Client
//
// # Patch OAuth 2.0 Client
//
// Patch an existing OAuth 2.0 Client using JSON Patch. If you pass `client_secret`
// the secret will be updated and returned via the API. This is the
// only time you will be able to retrieve the client secret, so write it down and keep it safe.
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are
// generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 200: oAuth2Client
// 404: errorOAuth2NotFound
// default: errorOAuth2Default
func (h *Handler) patchOAuth2Client(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
patchJSON, err := io.ReadAll(r.Body)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
id := ps.ByName("id")
c, err := h.r.ClientManager().GetConcreteClient(r.Context(), id)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
oldSecret := c.Secret
if err := jsonx.ApplyJSONPatch(patchJSON, c, "/id"); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
// fix for #2869
// GetConcreteClient returns a client with the hashed secret, however updateClient expects
// an empty secret if the secret hasn't changed. As such we need to check if the patch has
// updated the secret or not
if oldSecret == c.Secret {
c.Secret = ""
}
if err := h.updateClient(r.Context(), c, h.r.ClientValidator().Validate); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
h.r.Writer().Write(w, r, c)
}
// Paginated OAuth2 Client List Response
//
// swagger:response listOAuth2Clients
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type listOAuth2ClientsResponse struct {
tokenpagination.ResponseHeaders
// List of OAuth 2.0 Clients
//
// in:body
Body []Client
}
// Paginated OAuth2 Client List Parameters
//
// swagger:parameters listOAuth2Clients
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type listOAuth2ClientsParameters struct {
tokenpagination.RequestParameters
// The name of the clients to filter by.
//
// in: query
Name string `json:"client_name"`
// The owner of the clients to filter by.
//
// in: query
Owner string `json:"owner"`
}
// swagger:route GET /admin/clients oAuth2 listOAuth2Clients
//
// # List OAuth 2.0 Clients
//
// This endpoint lists all clients in the database, and never returns client secrets.
// As a default it lists the first 100 clients.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 200: listOAuth2Clients
// default: errorOAuth2Default
func (h *Handler) listOAuth2Clients(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
page, itemsPerPage := x.ParsePagination(r)
filters := Filter{
Limit: itemsPerPage,
Offset: page * itemsPerPage,
Name: r.URL.Query().Get("client_name"),
Owner: r.URL.Query().Get("owner"),
}
c, err := h.r.ClientManager().GetClients(r.Context(), filters)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
if c == nil {
c = []Client{}
}
for k := range c {
c[k].Secret = ""
}
total, err := h.r.ClientManager().CountClients(r.Context())
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
x.PaginationHeader(w, r.URL, int64(total), page, itemsPerPage)
h.r.Writer().Write(w, r, c)
}
// Get OAuth2 Client Parameters
//
// swagger:parameters getOAuth2Client
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type adminGetOAuth2Client struct {
// The id of the OAuth 2.0 Client.
//
// in: path
// required: true
ID string `json:"id"`
}
// swagger:route GET /admin/clients/{id} oAuth2 getOAuth2Client
//
// # Get an OAuth 2.0 Client
//
// Get an OAuth 2.0 client by its ID. This endpoint never returns the client secret.
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are
// generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 200: oAuth2Client
// default: errorOAuth2Default
func (h *Handler) Get(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var id = ps.ByName("id")
c, err := h.r.ClientManager().GetConcreteClient(r.Context(), id)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
c.Secret = ""
h.r.Writer().Write(w, r, c)
}
// Get OpenID Connect Dynamic Client Parameters
//
// swagger:parameters getOidcDynamicClient
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type getOidcDynamicClient struct {
// The id of the OAuth 2.0 Client.
//
// in: path
// required: true
ID string `json:"id"`
}
// swagger:route GET /oauth2/register/{id} oidc getOidcDynamicClient
//
// # Get OAuth2 Client using OpenID Dynamic Client Registration
//
// This endpoint behaves like the administrative counterpart (`getOAuth2Client`) but is capable of facing the
// public internet directly and can be used in self-service. It implements the OpenID Connect
// Dynamic Client Registration Protocol.
//
// To use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client
// uses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.
// If it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Security:
// bearer:
//
// Responses:
// 200: oAuth2Client
// default: errorOAuth2Default
func (h *Handler) getOidcDynamicClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if err := h.requireDynamicAuth(r); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
client, err := h.ValidDynamicAuth(r, ps)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
c, err := h.r.ClientManager().GetConcreteClient(r.Context(), client.GetID())
if err != nil {
err = herodot.ErrUnauthorized.WithReason("The requested OAuth 2.0 client does not exist or you did not provide the necessary credentials")
h.r.Writer().WriteError(w, r, err)
return
}
c.Secret = ""
c.Metadata = nil
h.r.Writer().Write(w, r, c)
}
// Delete OAuth2 Client Parameters
//
// swagger:parameters deleteOAuth2Client
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type deleteOAuth2Client struct {
// The id of the OAuth 2.0 Client.
//
// in: path
// required: true
ID string `json:"id"`
}
// swagger:route DELETE /admin/clients/{id} oAuth2 deleteOAuth2Client
//
// # Delete OAuth 2.0 Client
//
// Delete an existing OAuth 2.0 Client by its ID.
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are
// generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.
//
// Make sure that this endpoint is well protected and only callable by first-party components.
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 204: emptyResponse
// default: genericError
func (h *Handler) deleteOAuth2Client(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var id = ps.ByName("id")
if err := h.r.ClientManager().DeleteClient(r.Context(), id); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
w.WriteHeader(http.StatusNoContent)
}
// Set OAuth 2.0 Client Token Lifespans
//
// swagger:parameters setOAuth2ClientLifespans
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type setOAuth2ClientLifespans struct {
// OAuth 2.0 Client ID
//
// in: path
// required: true
ID string `json:"id"`
// in: body
Body Lifespans
}
// swagger:route PUT /admin/clients/{id}/lifespans oAuth2 setOAuth2ClientLifespans
//
// # Set OAuth2 Client Token Lifespans
//
// Set lifespans of different token types issued for this OAuth 2.0 client. Does not modify other fields.
//
// Consumes:
// - application/json
//
// Schemes: http, https
//
// Responses:
// 200: oAuth2Client
// default: genericError
func (h *Handler) setOAuth2ClientLifespans(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
var id = ps.ByName("id")
c, err := h.r.ClientManager().GetConcreteClient(r.Context(), id)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
var ls Lifespans
if err := json.NewDecoder(r.Body).Decode(&ls); err != nil {
h.r.Writer().WriteError(w, r, errorsx.WithStack(herodot.ErrBadRequest.WithReasonf("Unable to decode the request body: %s", err)))
return
}
c.Lifespans = ls
c.Secret = ""
if err := h.updateClient(r.Context(), c, h.r.ClientValidator().Validate); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
h.r.Writer().Write(w, r, c)
}
// swagger:parameters deleteOidcDynamicClient
//
//lint:ignore U1000 Used to generate Swagger and OpenAPI definitions
type dynamicClientRegistrationDeleteOAuth2Client struct {
// The id of the OAuth 2.0 Client.
//
// in: path
// required: true
ID string `json:"id"`
}
// swagger:route DELETE /oauth2/register/{id} oidc deleteOidcDynamicClient
//
// # Delete OAuth 2.0 Client using the OpenID Dynamic Client Registration Management Protocol
//
// This endpoint behaves like the administrative counterpart (`deleteOAuth2Client`) but is capable of facing the
// public internet directly and can be used in self-service. It implements the OpenID Connect
// Dynamic Client Registration Protocol. This feature needs to be enabled in the configuration. This endpoint
// is disabled by default. It can be enabled by an administrator.
//
// To use this endpoint, you will need to present the client's authentication credentials. If the OAuth2 Client
// uses the Token Endpoint Authentication Method `client_secret_post`, you need to present the client secret in the URL query.
// If it uses `client_secret_basic`, present the Client ID and the Client Secret in the Authorization header.
//
// OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are
// generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities.
//
// Produces:
// - application/json
//
// Schemes: http, https
//
// Security:
// bearer:
//
// Responses:
// 204: emptyResponse
// default: genericError
func (h *Handler) deleteOidcDynamicClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
if err := h.requireDynamicAuth(r); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
client, err := h.ValidDynamicAuth(r, ps)
if err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
if err := h.r.ClientManager().DeleteClient(r.Context(), client.GetID()); err != nil {
h.r.Writer().WriteError(w, r, err)
return
}
w.WriteHeader(http.StatusNoContent)
}
func (h *Handler) ValidDynamicAuth(r *http.Request, ps httprouter.Params) (fosite.Client, error) {
c, err := h.r.ClientManager().GetConcreteClient(r.Context(), ps.ByName("id"))
if err != nil {
return nil, herodot.ErrUnauthorized.
WithTrace(err).
WithReason("The requested OAuth 2.0 client does not exist or you provided incorrect credentials.").WithDebug(err.Error())
}
if len(c.RegistrationAccessTokenSignature) == 0 {
return nil, errors.WithStack(herodot.ErrUnauthorized.
WithReason("The requested OAuth 2.0 client does not exist or you provided incorrect credentials.").WithDebug("The OAuth2 Client does not have a registration access token."))
}
token := strings.TrimPrefix(fosite.AccessTokenFromRequest(r), "ory_at_")
if err := h.r.OAuth2HMACStrategy().ValidateAccessToken(
r.Context(),
// The strategy checks the expiry time of the token. Registration tokens don't expire (we don't have a way of
// rotating them) so we set the expiry time to a time in the future.
&fosite.Request{
Session: &fosite.DefaultSession{
ExpiresAt: map[fosite.TokenType]time.Time{
fosite.AccessToken: time.Now().Add(time.Hour),
},
},
RequestedAt: time.Now(),
},
token,
); err != nil {
return nil, herodot.ErrUnauthorized.
WithTrace(err).
WithReason("The requested OAuth 2.0 client does not exist or you provided incorrect credentials.").WithDebug(err.Error())
}
signature := h.r.OAuth2EnigmaStrategy().Signature(token)
if subtle.ConstantTimeCompare([]byte(c.RegistrationAccessTokenSignature), []byte(signature)) == 0 {
return nil, errors.WithStack(herodot.ErrUnauthorized.
WithReason("The requested OAuth 2.0 client does not exist or you provided incorrect credentials.").WithDebug("Registration access tokens do not match."))
}
return c, nil
}
func (h *Handler) requireDynamicAuth(r *http.Request) *herodot.DefaultError {
if !h.r.Config().PublicAllowDynamicRegistration(r.Context()) {
return herodot.ErrNotFound.WithReason("Dynamic registration is not enabled.")
}
return nil
}