Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC7662] Add introspect endpoint to introspect access & refresh token #3404

Merged
merged 10 commits into from
Mar 28, 2024
2 changes: 2 additions & 0 deletions server/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type discovery struct {
Keys string `json:"jwks_uri"`
UserInfo string `json:"userinfo_endpoint"`
DeviceEndpoint string `json:"device_authorization_endpoint"`
Introspect string `json:"introspection_endpoint"`
GrantTypes []string `json:"grant_types_supported"`
ResponseTypes []string `json:"response_types_supported"`
Subjects []string `json:"subject_types_supported"`
Expand All @@ -96,6 +97,7 @@ func (s *Server) discoveryHandler() (http.HandlerFunc, error) {
Keys: s.absURL("/keys"),
UserInfo: s.absURL("/userinfo"),
DeviceEndpoint: s.absURL("/device/code"),
Introspect: s.absURL("/introspect"),
supercairos marked this conversation as resolved.
Show resolved Hide resolved
Subjects: []string{"public"},
IDTokenAlgs: []string{string(jose.RS256)},
CodeChallengeAlgs: []string{codeChallengeMethodS256, codeChallengeMethodPlain},
Expand Down
70 changes: 70 additions & 0 deletions server/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,76 @@ func TestHandleHealth(t *testing.T) {
}
}

func TestHandleDiscovery(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

httpServer, server := newTestServer(ctx, t, nil)
defer httpServer.Close()

rr := httptest.NewRecorder()
server.ServeHTTP(rr, httptest.NewRequest("GET", "/.well-known/openid-configuration", nil))
if rr.Code != http.StatusOK {
t.Errorf("expected 200 got %d", rr.Code)
}

var res discovery
err := json.NewDecoder(rr.Result().Body).Decode(&res)
require.NoError(t, err)
require.Equal(t, discovery{
Issuer: httpServer.URL,
Auth: fmt.Sprintf("%s/auth", httpServer.URL),
Token: fmt.Sprintf("%s/token", httpServer.URL),
Keys: fmt.Sprintf("%s/keys", httpServer.URL),
UserInfo: fmt.Sprintf("%s/userinfo", httpServer.URL),
DeviceEndpoint: fmt.Sprintf("%s/device/code", httpServer.URL),
Introspect: fmt.Sprintf("%s/introspect", httpServer.URL),
GrantTypes: []string{
"authorization_code",
"refresh_token",
"urn:ietf:params:oauth:grant-type:device_code",
"urn:ietf:params:oauth:grant-type:token-exchange",
},
ResponseTypes: []string{
"code",
},
Subjects: []string{
"public",
},
IDTokenAlgs: []string{
"RS256",
},
CodeChallengeAlgs: []string{
"S256",
"plain",
},
Scopes: []string{
"openid",
"email",
"groups",
"profile",
"offline_access",
},
AuthMethods: []string{
"client_secret_basic",
"client_secret_post",
},
Claims: []string{
"iss",
"sub",
"aud",
"iat",
"exp",
"email",
"email_verified",
"locale",
"name",
"preferred_username",
"at_hash",
},
}, res)
}

func TestHandleHealthFailure(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand Down
82 changes: 82 additions & 0 deletions server/introspection.go
supercairos marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package server

type IntrospectionExtra struct {
AuthorizingParty string `json:"azp,omitempty"`

Email string `json:"email,omitempty"`
EmailVerified *bool `json:"email_verified,omitempty"`

Groups []string `json:"groups,omitempty"`

Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`

FederatedIDClaims *federatedIDClaims `json:"federated_claims,omitempty"`
}

// Introspection contains an access token's session data as specified by
// [IETF RFC 7662](https://tools.ietf.org/html/rfc7662)
type Introspection struct {
// Boolean indicator of whether or not the presented token
// is currently active. The specifics of a token's "active" state
// will vary depending on the implementation of the authorization
// server and the information it keeps about its tokens, but a "true"
// value return for the "active" property will generally indicate
// that a given token has been issued by this authorization server,
// has not been revoked by the resource owner, and is within its
// given time window of validity (e.g., after its issuance time and
// before its expiration time).
Active bool `json:"active"`

// JSON string containing a space-separated list of
// scopes associated with this token.
Scope string `json:"scope,omitempty"`

// Client identifier for the OAuth 2.0 client that
// requested this token.
ClientID string `json:"client_id"`

// Subject of the token, as defined in JWT [RFC7519].
// Usually a machine-readable identifier of the resource owner who
// authorized this token.
Subject string `json:"sub"`

// Integer timestamp, measured in the number of seconds
// since January 1 1970 UTC, indicating when this token will expire.
Expiry int64 `json:"exp"`

// Integer timestamp, measured in the number of seconds
// since January 1 1970 UTC, indicating when this token was
// originally issued.
IssuedAt int64 `json:"iat"`

// Integer timestamp, measured in the number of seconds
// since January 1 1970 UTC, indicating when this token is not to be
// used before.
NotBefore int64 `json:"nbf"`

// Human-readable identifier for the resource owner who
// authorized this token.
Username string `json:"username,omitempty"`

// Service-specific string identifier or list of string
// identifiers representing the intended audience for this token, as
// defined in JWT
Audience audience `json:"aud"`

// String representing the issuer of this token, as
// defined in JWT
Issuer string `json:"iss"`

// String identifier for the token, as defined in JWT [RFC7519].
JwtTokenID string `json:"jti,omitempty"`

// TokenType is the introspected token's type, typically `bearer`.
TokenType string `json:"token_type"`

// TokenUse is the introspected token's use, for example `access_token` or `refresh_token`.
TokenUse string `json:"token_use"`
supercairos marked this conversation as resolved.
Show resolved Hide resolved

// Extra is arbitrary data set from the token claims.
Extra IntrospectionExtra `json:"ext,omitempty"`
supercairos marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading