mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-07-18 08:49:54 +00:00
116 lines
3.7 KiB
Go
116 lines
3.7 KiB
Go
package oidc
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/ory/fosite"
|
|
)
|
|
|
|
type introspectionHandler struct {
|
|
provider fosite.OAuth2Provider
|
|
authenticator *federatedClientAuthenticator
|
|
baseURL string
|
|
}
|
|
|
|
func newIntrospectionHandler(provider fosite.OAuth2Provider, authenticator *federatedClientAuthenticator, baseURL string) *introspectionHandler {
|
|
return &introspectionHandler{
|
|
provider: provider,
|
|
authenticator: authenticator,
|
|
baseURL: baseURL,
|
|
}
|
|
}
|
|
|
|
// introspectToken godoc
|
|
// @Summary Introspect OIDC tokens
|
|
// @Description Pass a token to verify if it is considered valid.
|
|
// @Tags OIDC
|
|
// @Produce json
|
|
// @Param token formData string true "The token to be introspected."
|
|
// @Success 200 {object} object "Response with the introspection result."
|
|
// @Router /api/oidc/introspect [post]
|
|
func (h *introspectionHandler) introspectToken(c *gin.Context) {
|
|
ctx := c.Request.Context()
|
|
|
|
if h.tryFederatedClientAssertionIntrospection(c) {
|
|
return
|
|
}
|
|
|
|
response, err := h.provider.NewIntrospectionRequest(ctx, c.Request, NewEmptySession())
|
|
if err != nil {
|
|
slog.ErrorContext(ctx, "Failed to create introspection request", "error", err)
|
|
h.provider.WriteIntrospectionError(ctx, c.Writer, err)
|
|
return
|
|
}
|
|
|
|
// A client may only introspect its own tokens. If it was issued to another client, report it as
|
|
// inactive instead of leaking its existence or contents.
|
|
callerClientID, err := h.callerClientID(ctx, c)
|
|
if err != nil || callerClientID == "" || response.GetAccessRequester().GetClient().GetID() != callerClientID {
|
|
h.provider.WriteIntrospectionResponse(ctx, c.Writer, &fosite.IntrospectionResponse{Active: false})
|
|
return
|
|
}
|
|
|
|
h.provider.WriteIntrospectionResponse(ctx, c.Writer, response)
|
|
}
|
|
|
|
// tryFederatedClientAssertionIntrospection handles introspection requests authenticated
|
|
// with a federated client assertion passed as bearer token instead of client credentials.
|
|
func (h *introspectionHandler) tryFederatedClientAssertionIntrospection(c *gin.Context) bool {
|
|
ctx := c.Request.Context()
|
|
assertion := fosite.AccessTokenFromRequest(c.Request)
|
|
clientID := c.PostForm("client_id")
|
|
if assertion == "" || clientID == "" {
|
|
return false
|
|
}
|
|
|
|
client, err := h.authenticator.authenticateAssertion(ctx, assertion, clientID)
|
|
if err != nil {
|
|
h.provider.WriteIntrospectionError(ctx, c.Writer, fosite.ErrRequestUnauthorized.WithWrap(err))
|
|
return true
|
|
}
|
|
|
|
tokenUse, accessRequester, err := h.provider.IntrospectToken(ctx, c.PostForm("token"), fosite.TokenUse(c.PostForm("token_type_hint")), NewEmptySession(), strings.Fields(c.PostForm("scope"))...)
|
|
if err != nil {
|
|
h.provider.WriteIntrospectionError(ctx, c.Writer, fosite.ErrInactiveToken.WithWrap(err))
|
|
return true
|
|
}
|
|
|
|
response := &fosite.IntrospectionResponse{
|
|
Active: true,
|
|
AccessRequester: accessRequester,
|
|
TokenUse: tokenUse,
|
|
}
|
|
if tokenUse == fosite.AccessToken {
|
|
response.AccessTokenType = fosite.BearerAccessToken
|
|
}
|
|
|
|
if accessRequester.GetClient().GetID() != client.GetID() {
|
|
h.provider.WriteIntrospectionResponse(ctx, c.Writer, &fosite.IntrospectionResponse{Active: false})
|
|
return true
|
|
}
|
|
|
|
h.provider.WriteIntrospectionResponse(ctx, c.Writer, response)
|
|
return true
|
|
}
|
|
|
|
// callerClientID resolves the client that authenticated this introspection request.
|
|
func (h *introspectionHandler) callerClientID(ctx context.Context, c *gin.Context) (string, error) {
|
|
if bearer := fosite.AccessTokenFromRequest(c.Request); bearer != "" {
|
|
_, accessRequester, err := h.provider.IntrospectToken(ctx, bearer, fosite.AccessToken, NewEmptySession())
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return accessRequester.GetClient().GetID(), nil
|
|
}
|
|
|
|
if id, _, ok := c.Request.BasicAuth(); ok {
|
|
return url.QueryUnescape(id)
|
|
}
|
|
|
|
return "", nil
|
|
}
|