summaryrefslogtreecommitdiff
path: root/bin/idp
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-03-17 21:38:32 -0600
committermo khan <mo@mokhan.ca>2025-03-17 21:38:32 -0600
commita255f9818c4fab5c2e1c3e9ded61d37991b2cebb (patch)
tree91fb5be83ec57fdddb6515d69de01fec36f078ea /bin/idp
parent66edae4510b51570e6e4a21ef38dfc7defb63982 (diff)
feat: add a token introspection endpoint
Diffstat (limited to 'bin/idp')
-rwxr-xr-xbin/idp47
1 files changed, 35 insertions, 12 deletions
diff --git a/bin/idp b/bin/idp
index d612e79..0e16817 100755
--- a/bin/idp
+++ b/bin/idp
@@ -105,7 +105,7 @@ module Authn
end
def create_access_token
- ::Authz::JWT.new(sub: to_global_id.to_s, iat: Time.now.to_i)
+ ::Authz::JWT.new(sub: to_global_id.to_s)
end
def assertion_attributes_for(request)
@@ -323,10 +323,30 @@ module Authz
end
class JWT
+ class << self
+ # TODO:: validate signature
+ def decode(encoded)
+ _header, body, _signature = encoded
+ .split('.', 3)
+ .map { |x| JSON.parse(Base64.strict_decode64(x), symbolize_names: true) rescue {} }
+ new(body)
+ end
+ end
+
attr_reader :claims
def initialize(claims)
- @claims = claims
+ now = Time.now.to_i
+ @claims = {
+ iat: now,
+ nbf: now,
+ jti: SecureRandom.uuid,
+ }.merge(claims)
+ end
+
+ def active?
+ # TODO:: check if current time is within valid range
+ true
end
def to_jwt
@@ -360,21 +380,14 @@ module Authz
false
end
- def subject_of(token)
- _header, claims, _signature = from_jwt(token)
- claims[:sub]
+ def subject_of(encoded_token)
+ token = ::Authz::JWT.decode(encoded_token)
+ token&.claims[:sub]
end
def resource_from(global_id)
GlobalID::Locator.locate(global_id)
end
-
- # TODO:: validate signature
- def from_jwt(token)
- token
- .split('.', 3)
- .map { |x| JSON.parse(Base64.strict_decode64(x), symbolize_names: true) rescue {} }
- end
end
end
@@ -499,6 +512,9 @@ module Authz
case request.path
when "/oauth/authorize" # RFC-6749
return post_authorize(request)
+ when "/oauth/introspect" # RFC-7662
+ params = request.content_type == "application/json" ? JSON.parse(request.body.read, symbolize_names: true) : Hash[URI.decode_www_form(request.body.read)].transform_keys(&:to_sym)
+ return post_introspect(params.slice(:token, :token_type_hint))
when "/oauth/token" # RFC-6749
params = request.content_type == "application/json" ? JSON.parse(request.body.read, symbolize_names: true) : Hash[URI.decode_www_form(request.body.read)].transform_keys(&:to_sym)
grant = AuthorizationGrant.find_by(params)
@@ -515,6 +531,13 @@ module Authz
http_not_found
end
+ private
+
+ def post_introspect(params)
+ token = ::Authz::JWT.decode(params[:token])
+ return [200, { "Content-Type" => "application/json" }, [JSON.pretty_generate(token.claims.merge(active: token.active?))]]
+ end
+
def get_authorize(oauth_params)
template = <<~ERB
<!DOCTYPE html>