main
1# frozen_string_literal: true
2
3module Oauth
4 class TokensController < ActionController::API
5 include ActionController::HttpAuthentication::Basic::ControllerMethods
6 before_action :authenticate!
7
8 def create
9 response.headers['Cache-Control'] = 'no-store'
10 response.headers['Pragma'] = 'no-cache'
11
12 @access_token, @refresh_token = tokens_for(params[:grant_type])
13 return bad_request if @access_token.nil?
14
15 render formats: :json
16 rescue StandardError => error
17 Rails.logger.error(error)
18 bad_request
19 end
20
21 def introspect
22 claims = Token.claims_for(params[:token], token_type: :any)
23 if claims.empty? || Token.revoked?(claims[:jti])
24 render json: { active: false }, status: :ok
25 else
26 render json: claims.merge(active: true), status: :ok
27 end
28 end
29
30 def revoke
31 claims = Token.claims_for(params[:token], token_type: :any)
32 current_client.revoke(Token.find(claims[:jti])) unless claims.empty?
33 render plain: "", status: :ok
34 rescue StandardError => error
35 logger.error(error)
36 render plain: "", status: :ok
37 end
38
39 private
40
41 attr_reader :current_client
42
43 def authenticate!
44 @current_client = authenticate_with_http_basic do |id, client_secret|
45 Client.find(id)&.authenticate(client_secret)
46 end
47 return if current_client
48
49 render "invalid_client", formats: :json, status: :unauthorized
50 end
51
52 def bad_request
53 render "bad_request", formats: :json, status: :bad_request
54 end
55
56 def authorization_code_grant(code, verifier)
57 authorization = current_client.authorizations.active.find_by!(code: code)
58 return unless authorization.valid_verifier?(verifier)
59
60 authorization.issue_tokens_to(current_client)
61 end
62
63 def refresh_grant(refresh_token)
64 jti = Token.claims_for(refresh_token, token_type: :refresh)[:jti]
65 token = Token.find(jti)
66 token.issue_tokens_to(current_client)
67 end
68
69 def password_grant(username, password)
70 user = User.login(username, password)
71 user.issue_tokens_to(current_client)
72 end
73
74 def saml_assertion_grant(raw)
75 assertion = Saml::Kit::Assertion.new(
76 Base64.urlsafe_decode64(raw)
77 )
78 return if assertion.invalid?
79
80 user = if assertion.name_id_format == Saml::Kit::Namespaces::PERSISTENT
81 User.find(assertion.name_id)
82 else
83 User.find_by!(email: assertion.name_id)
84 end
85 user.issue_tokens_to(current_client)
86 end
87
88 def tokens_for(grant_type = params[:grant_type])
89 case grant_type
90 when 'authorization_code'
91 authorization_code_grant(params[:code], params[:code_verifier])
92 when 'refresh_token'
93 refresh_grant(params[:refresh_token])
94 when 'client_credentials'
95 [current_client.access_token, nil]
96 when 'password'
97 password_grant(params[:username], params[:password])
98 when 'urn:ietf:params:oauth:grant-type:saml2-bearer' # RFC7522
99 saml_assertion_grant(params[:assertion])
100 # when 'urn:ietf:params:oauth:grant-type:jwt-bearer' # RFC7523
101 # raise NotImplementedError
102 end
103 end
104 end
105end