Commit cb239d2
Changed files (6)
app
config
spec
requests
app/controllers/clients_controller.rb
@@ -0,0 +1,27 @@
+class ClientsController < ApplicationController
+ skip_before_action :authenticate!
+ before_action :apply_cache_headers
+
+ def create
+ @client = Client.create!(transform(secure_params))
+ render status: :created, formats: :json
+ end
+
+ private
+
+ def secure_params
+ params.permit(:client_name, :token_endpoint_auth_method, :logo_uri, :jwks_uri, redirect_uris: [])
+ end
+
+ def transform(params)
+ {
+ name: params[:client_name],
+ redirect_uri: params[:redirect_uris][0],
+ }
+ end
+
+ def apply_cache_headers
+ response.headers["Cache-Control"] = "no-cache, no-store"
+ response.headers["Pragma"] = "no-cache"
+ end
+end
app/models/client.rb
@@ -15,6 +15,18 @@ class Client < ApplicationRecord
self.password = SecureRandom.base58(24) unless password_digest
end
+ def redirect_uris
+ [redirect_uri]
+ end
+
+ def grant_types
+ [:authorization_code, :refresh_token, :client_credentials, :password, 'urn:ietf:params:oauth:grant-type:saml2-bearer']
+ end
+
+ def token_endpoint_auth_method
+ :client_secret_basic
+ end
+
def access_token
transaction do
Token
app/views/clients/create.json.jbuilder
@@ -0,0 +1,10 @@
+json.client_id @client.to_param
+json.client_secret @client.password
+json.client_id_issued_at @client.created_at.to_i
+json.client_secret_expires_at 0
+json.redirect_uris @client.redirect_uris
+json.grant_types @client.grant_types
+json.client_name @client.name
+json.token_endpoint_auth_method @client.token_endpoint_auth_method
+json.logo_uri @client.logo_uri
+json.jwks_uri @client.jwks_uri
config/routes.rb
@@ -9,6 +9,7 @@ Rails.application.routes.draw do
get :authorize, to: "oauths#show"
end
resource :session, only: [:new, :create, :destroy]
+ resources :clients, only: [:create]
resources :registrations, only: [:new, :create]
resource :response, only: [:show]
resource :tokens, only: [:create] do
spec/requests/clients_spec.rb
@@ -0,0 +1,71 @@
+require 'rails_helper'
+
+RSpec.describe "/clients" do
+ describe "POST /clients" do
+ let(:redirect_uris) { [generate(:uri), generate(:uri)] }
+ let(:client_name) { FFaker::Name.name }
+ let(:logo_uri) { generate(:uri) }
+ let(:jwks_uri) { generate(:uri) }
+ let(:json) { JSON.parse(response.body, symbolize_names: true) }
+ let(:last_client) { Client.order(created_at: :asc).last }
+
+ context "when the registration request is valid" do
+ before do
+ post "/clients", params: {
+ redirect_uris: redirect_uris,
+ client_name: client_name,
+ token_endpoint_auth_method: :client_secret_basic,
+ logo_uri: logo_uri,
+ jwks_uri: jwks_uri,
+ }
+ end
+
+ specify { expect(response).to have_http_status(:created) }
+ specify { expect(response.headers['Content-Type']).to include("application/json") }
+ specify { expect(response.headers['Cache-Control']).to include("no-store") }
+ specify { expect(response.headers['Pragma']).to eql("no-cache") }
+ specify { expect(json[:client_id]).to eql(last_client.uuid) }
+ specify { expect(json[:client_secret]).to be_present }
+ specify { expect(json[:client_id_issued_at]).to eql(last_client.created_at.to_i) }
+ specify { expect(json[:client_secret_expires_at]).to be_zero }
+ specify { expect(json[:redirect_uris]).to match_array(redirect_uris) }
+ specify { expect(json[:grant_types]).to match_array(last_client.grant_types.map(&:to_s)) }
+ specify { expect(json[:client_name]).to eql(client_name) }
+ specify { expect(json[:token_endpoint_auth_method]).to eql('client_secret_basic') }
+ specify { expect(json[:logo_uri]).to eql(logo_uri) }
+ specify { expect(json[:jwks_uri]).to eql(jwks_uri) }
+ end
+
+ context "when the registrations is missing valid redirect_uris" do
+ before do
+ post "/clients", params: {
+ redirect_uris: [],
+ client_name: client_name,
+ token_endpoint_auth_method: :client_secret_basic,
+ logo_uri: logo_uri,
+ jwks_uri: jwks_uri,
+ }
+ end
+
+ specify { expect(response).to have_http_status(:bad_request) }
+ specify { expect(json[:error]).to eql("invalid_redirect_uri") }
+ specify { expect(json[:error_description]).to be_present }
+ end
+
+ context "when the registration request is missing a client name" do
+ before do
+ post "/clients", params: {
+ redirect_uris: redirect_uris,
+ client_name: "",
+ token_endpoint_auth_method: :client_secret_basic,
+ logo_uri: logo_uri,
+ jwks_uri: jwks_uri,
+ }
+ end
+
+ specify { expect(response).to have_http_status(:bad_request) }
+ specify { expect(json[:error]).to eql("invalid_client_metadata") }
+ specify { expect(json[:error_description]).to be_present }
+ end
+ end
+end
spec/factories.rb
@@ -3,4 +3,5 @@
FactoryBot.define do
sequence(:email) { |_n| FFaker::Internet.email }
sequence(:password) { |_n| FFaker::Internet.password }
+ sequence(:uri) { |_n| FFaker::Internet.uri('https') }
end