Commit af35858

mo <mo@mokhan.ca>
2018-10-12 00:40:42
add support for implicit grant
1 parent 31215fc
Changed files (4)
app
spec
app/controllers/oauths_controller.rb
@@ -2,17 +2,36 @@
 
 class OauthsController < ApplicationController
   def show
-    return render_error(:not_found) unless params[:response_type] == 'code'
+    return render_error(:not_found) unless params[:response_type] == 'code' || params[:response_type] == 'token'
 
     @client = Client.find_by!(uuid: params[:client_id])
+    session[:oauth] = {
+      client_id: params[:client_id],
+      response_type: params[:response_type],
+      state: params[:state],
+    }
+
   end
 
   def create
-    client = Client.find_by!(uuid: params[:client_id])
+    client = Client.find_by!(uuid: session[:oauth][:client_id])
     authorization = client.authorizations.create!(user: current_user)
-    redirect_to client.redirect_uri_path(
-      code: authorization.code,
-      state: params[:state]
-    )
+
+    if 'code' == session[:oauth][:response_type]
+      redirect_to client.redirect_uri_path(
+        code: authorization.code,
+        state: session[:oauth][:state]
+      )
+    elsif 'token' == session[:oauth][:response_type]
+      @access_token, _ = authorization.issue_tokens_to(client)
+
+      redirect_to client.redirect_uri_path(
+        access_token: @access_token.to_jwt,
+        token_type: "Bearer",
+        expires_in: 5.minutes,
+        scope: "admin",
+        state: session[:oauth][:state]
+      )
+    end
   end
 end
app/models/client.rb
@@ -27,8 +27,16 @@ class Client < ApplicationRecord
     uuid
   end
 
-  def redirect_uri_path(code:, state: nil)
-    result = redirect_uri + '?code=' + code
+  def redirect_uri_path(code: nil, access_token: nil, token_type: "Bearer", expires_in: nil, scope: "", state: nil)
+    result = redirect_uri
+    if code
+      result += '?code=' + code
+    elsif access_token
+      result += '#access_token=' + access_token
+      result += "&token_type=#{token_type}"
+      result += "&expires_in=#{expires_in.seconds.to_i}" if expires_in.present?
+      result += "&scope=#{scope}" if scope.present?
+    end
     result += "&state=#{state}" if state.present?
     result
   end
app/views/oauths/show.html.erb
@@ -4,8 +4,6 @@
       <h1><%= t('.title') %></h1>
       <p><%= t('.authorize_prompt_html', name: @client.name) %></p>
       <%= form_for :authorization, url: oauth_path, method: :post do |form| %>
-        <%= hidden_field_tag :client_id, @client.to_param %>
-        <%= hidden_field_tag :state, params[:state] %>
         <%= form.button t('.authorize'), type: 'submit', class: 'btn btn-primary', data: { disable_with: t('loading') } %>
       <% end %>
     </div>
spec/requests/oauth_spec.rb
@@ -12,11 +12,16 @@ RSpec.describe '/oauth' do
       context "when the client id is known" do
         let(:client) { create(:client) }
 
-        context "when the correct parameters are provided" do
+        context "when requesting an authorization code" do
           before { get "/oauth", params: { client_id: client.to_param, response_type: 'code', state: state } }
           specify { expect(response).to have_http_status(:ok) }
           specify { expect(response.body).to include(CGI.escapeHTML(client.name)) }
-          specify { expect(response.body).to include(state) }
+        end
+
+        context "when requesting an access token" do
+          before { get "/oauth", params: { client_id: client.to_param, response_type: 'token', state: state } }
+          specify { expect(response).to have_http_status(:ok) }
+          specify { expect(response.body).to include(CGI.escapeHTML(client.name)) }
         end
 
         context "when an incorrect response_type is provided" do
@@ -36,7 +41,6 @@ RSpec.describe '/oauth' do
 
         specify { expect(response).to have_http_status(:ok) }
         specify { expect(response.body).to include(CGI.escapeHTML(client.name)) }
-        specify { expect(response.body).to include(state) }
       end
     end
 
@@ -45,9 +49,29 @@ RSpec.describe '/oauth' do
         let(:client) { create(:client) }
         let(:state) { SecureRandom.uuid }
 
-        before { post "/oauth", params: { client_id: client.to_param, state: state } }
+        context "when the client requested an authorization code" do
+          before :each do
+            get "/oauth", params: { client_id: client.to_param, response_type: 'code', state: state }
+            post "/oauth"
+          end
 
-        specify { expect(response).to redirect_to(client.redirect_uri_path(code: Authorization.last.code, state: state)) }
+          specify { expect(response).to redirect_to(client.redirect_uri_path(code: Authorization.last.code, state: state)) }
+        end
+
+        context "when the client requested a token" do
+          let(:token) { Token.access.active.last&.to_jwt }
+          let(:scope) { "admin" }
+
+          before :each do
+            get "/oauth", params: { client_id: client.to_param, response_type: 'token', state: state }
+            post "/oauth"
+          end
+
+          specify do
+            expected_url = "#{client.redirect_uri}#access_token=#{token}&token_type=Bearer&expires_in=300&scope=#{scope}&state=#{state}"
+            expect(response).to redirect_to(expected_url)
+          end
+        end
       end
     end
   end