lib/rodauth/features/oauth_token_introspection.rb in rodauth-oauth-0.10.4 vs lib/rodauth/features/oauth_token_introspection.rb in rodauth-oauth-1.0.0.pre.beta1
- old
+ new
@@ -1,74 +1,92 @@
# frozen_string_literal: true
+require "rodauth/oauth"
+require "rodauth/oauth/http_extensions"
+
module Rodauth
Feature.define(:oauth_token_introspection, :OauthTokenIntrospection) do
depends :oauth_base
before "introspect"
auth_value_methods(
- :before_introspection_request
+ :resource_owner_identifier
)
# /introspect
- route(:introspect) do |r|
- next unless is_authorization_server?
-
+ auth_server_route(:introspect) do |r|
+ require_oauth_application_for_introspect
before_introspect_route
- require_oauth_application
r.post do
catch_error do
- validate_oauth_introspect_params
+ validate_introspect_params
+ token_type_hint = param_or_nil("token_type_hint")
+
before_introspect
- oauth_token = case param("token_type_hint")
- when "access_token"
- oauth_token_by_token(param("token"))
+ oauth_grant = case token_type_hint
+ when "access_token", nil
+ if features.include?(:oauth_jwt) && oauth_jwt_access_tokens
+ jwt_decode(param("token"))
+ else
+ oauth_grant_by_token(param("token"))
+ end
when "refresh_token"
- oauth_token_by_refresh_token(param("token"))
- else
- oauth_token_by_token(param("token")) || oauth_token_by_refresh_token(param("token"))
+ oauth_grant_by_refresh_token(param("token"))
end
- if oauth_application
- redirect_response_error("invalid_request") if oauth_token && !token_from_application?(oauth_token, oauth_application)
- elsif oauth_token
- @oauth_application = db[oauth_applications_table].where(oauth_applications_id_column =>
- oauth_token[oauth_tokens_oauth_application_id_column]).first
- end
+ oauth_grant ||= oauth_grant_by_refresh_token(param("token")) if token_type_hint.nil?
- json_response_success(json_token_introspect_payload(oauth_token))
+ json_response_success(json_token_introspect_payload(oauth_grant))
end
- throw_json_response_error(invalid_oauth_response_status, "invalid_request")
+ throw_json_response_error(oauth_invalid_response_status, "invalid_request")
end
end
# Token introspect
- def validate_oauth_introspect_params(token_hint_types = %w[access_token refresh_token].freeze)
+ def validate_introspect_params(token_hint_types = %w[access_token refresh_token].freeze)
# check if valid token hint type
if param_or_nil("token_type_hint") && !token_hint_types.include?(param("token_type_hint"))
redirect_response_error("unsupported_token_type")
end
redirect_response_error("invalid_request") unless param_or_nil("token")
end
- def json_token_introspect_payload(token)
- return { active: false } unless token
+ def json_token_introspect_payload(grant_or_claims)
+ return { active: false } unless grant_or_claims
- {
- active: true,
- scope: token[oauth_tokens_scopes_column],
- client_id: oauth_application[oauth_applications_client_id_column],
- # username
- token_type: oauth_token_type,
- exp: token[oauth_tokens_expires_in_column].to_i
- }
+ if grant_or_claims["sub"]
+ # JWT
+ {
+ active: true,
+ scope: grant_or_claims["scope"],
+ client_id: grant_or_claims["client_id"],
+ username: resource_owner_identifier(grant_or_claims),
+ token_type: "access_token",
+ exp: grant_or_claims["exp"],
+ iat: grant_or_claims["iat"],
+ nbf: grant_or_claims["nbf"],
+ sub: grant_or_claims["sub"],
+ aud: grant_or_claims["aud"],
+ iss: grant_or_claims["iss"],
+ jti: grant_or_claims["jti"]
+ }
+ else
+ {
+ active: true,
+ scope: grant_or_claims[oauth_grants_scopes_column],
+ client_id: oauth_application[oauth_applications_client_id_column],
+ username: resource_owner_identifier(grant_or_claims),
+ token_type: oauth_token_type,
+ exp: grant_or_claims[oauth_grants_expires_in_column].to_i
+ }
+ end
end
def check_csrf?
case request.path
when introspect_path
@@ -78,32 +96,44 @@
end
end
private
- def introspection_request(token_type_hint, token)
- auth_url = URI(authorization_server_url)
- http = Net::HTTP.new(auth_url.host, auth_url.port)
- http.use_ssl = auth_url.scheme == "https"
+ def require_oauth_application_for_introspect
+ (token = ((v = request.env["HTTP_AUTHORIZATION"]) && v[/\A *Bearer (.*)\Z/, 1]))
- request = Net::HTTP::Post.new(auth_url.path + introspect_path)
- request["content-type"] = "application/x-www-form-urlencoded"
- request["accept"] = json_response_content_type
- request.set_form_data({ "token_type_hint" => token_type_hint, "token" => token })
+ return require_oauth_application unless token
- before_introspection_request(request)
- response = http.request(request)
- authorization_required unless response.code.to_i == 200
+ oauth_application = current_oauth_application
- JSON.parse(response.body)
+ authorization_required unless oauth_application
+
+ @oauth_application = oauth_application
end
- def before_introspection_request(request); end
-
def oauth_server_metadata_body(*)
super.tap do |data|
data[:introspection_endpoint] = introspect_url
data[:introspection_endpoint_auth_methods_supported] = %w[client_secret_basic]
+ end
+ end
+
+ def resource_owner_identifier(grant_or_claims)
+ if (account_id = grant_or_claims[oauth_grants_account_id_column])
+ account_ds(account_id).select(login_column).first[login_column]
+ elsif (app_id = grant_or_claims[oauth_grants_oauth_application_id_column])
+ db[oauth_applications_table].where(oauth_applications_id_column => app_id)
+ .select(oauth_applications_name_column)
+ .first[oauth_applications_name_column]
+ elsif (subject = grant_or_claims["sub"])
+ # JWT
+ if subject == grant_or_claims["client_id"]
+ db[oauth_applications_table].where(oauth_applications_client_id_column => subject)
+ .select(oauth_applications_name_column)
+ .first[oauth_applications_name_column]
+ else
+ account_ds(subject).select(login_column).first[login_column]
+ end
end
end
end
end