Add support for access tokens in implicit flow

Squashed commits:
[7dbf618] Use Rail's find_or_create_by method
This commit is contained in:
theworldbright 2015-07-16 01:53:10 +09:00
parent 2d762da072
commit ee9ac06e1a
6 changed files with 73 additions and 43 deletions

View file

@ -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])

View file

@ -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,6 +15,11 @@ class OpenidConnect::Authorization < ActiveRecord::Base
def create_access_token
o_auth_access_tokens.create!.bearer_token
# TODO: Add support for request object
end
def create_id_token(nonce)
id_tokens.create!(nonce: nonce)
end
def self.find_by_client_id_and_user(client_id, user)
@ -22,15 +27,5 @@ class OpenidConnect::Authorization < ActiveRecord::Base
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)
end
# TODO: Handle creation error
def self.find_or_create(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)
end
# TODO: Consider splitting into subclasses by flow type
end

View file

@ -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) }

View file

@ -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)

View file

@ -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

View file

@ -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,6 +113,36 @@ describe OpenidConnect::AuthorizationsController, type: :controller do
end
describe "#create" do
context "when id_token token" do
before do
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
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 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 id_token" 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
@ -150,4 +180,6 @@ describe OpenidConnect::AuthorizationsController, type: :controller do
end
end
end
end
end