Skip to content
Extraits de code Groupes Projets
Valider ee9ac06e rédigé par theworldbright's avatar theworldbright
Parcourir les fichiers

Add support for access tokens in implicit flow

Squashed commits:
[7dbf618] Use Rail's find_or_create_by method
parent 2d762da0
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
......@@ -76,7 +76,7 @@ class OpenidConnect::AuthorizationsController < ApplicationController
req = Rack::Request.new(request.env)
req.update_param("client_id", session[:client_id])
req.update_param("redirect_uri", session[:redirect_uri])
req.update_param("response_type", session[:response_type])
req.update_param("response_type", session[:response_type].respond_to?(:map) ? session[:response_type].map(&:to_s).join(" ") : session[:response_type])
req.update_param("scopes", session[:scopes])
req.update_param("request_object", session[:request_object])
req.update_param("nonce", session[:nonce])
......
......@@ -2,12 +2,12 @@ class OpenidConnect::Authorization < ActiveRecord::Base
belongs_to :user
belongs_to :o_auth_application
validates :user, presence: true, uniqueness: true
validates :o_auth_application, presence: true, uniqueness: true
validates :user, presence: true
validates :o_auth_application, presence: true
has_many :scopes, through: :authorization_scopes
has_many :o_auth_access_tokens, dependent: :destroy
has_many :id_tokens
has_many :id_tokens, dependent: :destroy
def generate_refresh_token
self.refresh_token = SecureRandom.hex(32)
......@@ -15,21 +15,16 @@ class OpenidConnect::Authorization < ActiveRecord::Base
def create_access_token
o_auth_access_tokens.create!.bearer_token
# TODO: Add support for request object
end
def self.find_by_client_id_and_user(client_id, user)
app = OpenidConnect::OAuthApplication.find_by(client_id: client_id)
find_by(o_auth_application: app, user: user)
end
def self.find_by_app_and_user(app, user)
find_by(o_auth_application: app, user: user)
def create_id_token(nonce)
id_tokens.create!(nonce: nonce)
end
# TODO: Handle creation error
def self.find_or_create(client_id, user)
def self.find_by_client_id_and_user(client_id, user)
app = OpenidConnect::OAuthApplication.find_by(client_id: client_id)
find_by_app_and_user(app, user) || create!(user: user, o_auth_application: app)
find_by(o_auth_application: app, user: user)
end
# TODO: Consider splitting into subclasses by flow type
......
......@@ -5,7 +5,7 @@ class OpenidConnect::OAuthAccessToken < ActiveRecord::Base
before_validation :setup, on: :create
validates :token, presence: true, uniqueness: true
validates :authorization, presence: true, uniqueness: true
validates :authorization, presence: true
scope :valid, ->(time) { where("expires_at >= ?", time) }
......
......@@ -16,7 +16,7 @@ class OpenidConnect::OAuthApplication < ActiveRecord::Base
class << self
def available_response_types
["id_token"]
["id_token", "id_token token"]
end
def register!(registrar)
......
......@@ -18,14 +18,17 @@ module OpenidConnect
end
end
# TODO: Add support for request object and auth code
def approved!(req, res)
auth = OpenidConnect::Authorization.find_or_create(req.client_id, @user)
auth = OpenidConnect::Authorization.find_or_create_by(o_auth_application: @o_auth_application, user: @user)
response_types = Array(req.response_type)
if response_types.include?(:token)
res.access_token = auth.create_access_token
end
if response_types.include?(:id_token)
id_token = auth.id_tokens.create!(nonce: req.nonce)
options = %i(code access_token).map{|option| ["res.#{option}", res.respond_to?(option) ? res.option : nil]}.to_h
res.id_token = id_token.to_jwt(options)
# TODO: Add support for request object
id_token = auth.create_id_token(req.nonce)
access_token_value = res.respond_to?(:access_token) ? res.access_token : nil
res.id_token = id_token.to_jwt(code: nil, access_token: access_token_value)
end
res.approve!
end
......
......@@ -89,7 +89,7 @@ describe OpenidConnect::AuthorizationsController, type: :controller do
end
end
context "when already authorized" do
let!(:auth) { OpenidConnect::Authorization.find_or_create(client.client_id, alice) }
let!(:auth) { OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: alice) }
context "when valid parameters are passed" do
before do
......@@ -113,41 +113,73 @@ describe OpenidConnect::AuthorizationsController, type: :controller do
end
describe "#create" do
before do
get :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/", response_type: "id_token",
scope: "openid", nonce: 418_093_098_3, state: 418_093_098_3
end
context "when authorization is approved" do
context "when id_token token" do
before do
post :create, approve: "true"
get :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/", response_type: "id_token token",
scope: "openid", nonce: 418_093_098_3, state: 418_093_098_3
end
it "should return the id token in a fragment" do
expect(response.location).to have_content("id_token=")
encoded_id_token = response.location[/(?<=id_token=)[^&]+/]
decoded_token = OpenIDConnect::ResponseObject::IdToken.decode encoded_id_token, OpenidConnect::IdTokenConfig.public_key
expect(decoded_token.nonce).to eq("4180930983")
expect(decoded_token.exp).to be > Time.now.utc.to_i
end
context "when authorization is approved" do
before do
post :create, approve: "true"
end
it "should return the id token in a fragment" do
encoded_id_token = response.location[/(?<=id_token=)[^&]+/]
decoded_token = OpenIDConnect::ResponseObject::IdToken.decode encoded_id_token, OpenidConnect::IdTokenConfig.public_key
expect(decoded_token.nonce).to eq("4180930983")
expect(decoded_token.exp).to be > Time.now.utc.to_i
end
it "should return the passed in state" do
expect(response.location).to have_content("state=4180930983")
it "should return a valid access token in a fragment" do
encoded_id_token = response.location[/(?<=id_token=)[^&]+/]
decoded_token = OpenIDConnect::ResponseObject::IdToken.decode encoded_id_token, OpenidConnect::IdTokenConfig.public_key
access_token = response.location[/(?<=access_token=)[^&]+/]
access_token_check_num = UrlSafeBase64.encode64(OpenSSL::Digest::SHA256.digest(access_token)[0, 128 / 8])
expect(decoded_token.at_hash).to eq(access_token_check_num)
end
end
end
context "when authorization is denied" do
context "when id_token" do
before do
post :create, approve: "false"
get :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/", response_type: "id_token",
scope: "openid", nonce: 418_093_098_3, state: 418_093_098_3
end
it "should return an error in the fragment" do
expect(response.location).to have_content("error=")
context "when authorization is approved" do
before do
post :create, approve: "true"
end
it "should return the id token in a fragment" do
expect(response.location).to have_content("id_token=")
encoded_id_token = response.location[/(?<=id_token=)[^&]+/]
decoded_token = OpenIDConnect::ResponseObject::IdToken.decode encoded_id_token, OpenidConnect::IdTokenConfig.public_key
expect(decoded_token.nonce).to eq("4180930983")
expect(decoded_token.exp).to be > Time.now.utc.to_i
end
it "should return the passed in state" do
expect(response.location).to have_content("state=4180930983")
end
end
it "should NOT contain a id token in the fragment" do
expect(response.location).to_not have_content("id_token=")
context "when authorization is denied" do
before do
post :create, approve: "false"
end
it "should return an error in the fragment" do
expect(response.location).to have_content("error=")
end
it "should NOT contain a id token in the fragment" do
expect(response.location).to_not have_content("id_token=")
end
end
end
end
end
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter