Skip to content

Commit

Permalink
Merge 08969a2 into 876abca
Browse files Browse the repository at this point in the history
  • Loading branch information
hiyosi committed Aug 29, 2014
2 parents 876abca + 08969a2 commit 9afd086
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 49 deletions.
2 changes: 1 addition & 1 deletion lib/omniauth/openid_connect/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module OmniAuth
module OpenIDConnect
VERSION = "0.1.0"
VERSION = "0.1.1"
end
end
199 changes: 162 additions & 37 deletions lib/omniauth/strategies/openid_connect.rb
Original file line number Diff line number Diff line change
@@ -1,30 +1,38 @@
require 'addressable/uri'
require "net/http"
require 'timeout'
require 'net/http'
require 'open-uri'
require 'omniauth'
require "openid_connect"
require 'openid_connect'

module OmniAuth
module Strategies
class OpenIDConnect
include OmniAuth::Strategy

option :client_options, {
identifier: nil,
secret: nil,
redirect_uri: nil,
scheme: "https",
host: nil,
port: 443,
authorization_endpoint: "/authorize",
token_endpoint: "/token",
userinfo_endpoint: "/userinfo"
identifier: nil,
secret: nil,
redirect_uri: nil,
scheme: "https",
host: nil,
port: 443,
authorization_endpoint: "/authorize",
token_endpoint: "/token",
userinfo_endpoint: "/userinfo",
jwks_uri: '/jwk'
}
option :issuer
option :discovery, false
option :client_signing_alg
option :client_jwk_signing_key
option :client_x509_signing_key
option :scope, [:openid]
option :response_type, "code"
option :state
option :response_mode
option :display, nil#, [:page, :popup, :touch, :wap]
option :prompt, nil#, [:none, :login, :consent, :select_account]
option :display, nil #, [:page, :popup, :touch, :wap]
option :prompt, nil #, [:none, :login, :consent, :select_account]
option :max_age
option :ui_locales
option :id_token_hint
Expand All @@ -35,80 +43,197 @@ class OpenIDConnect

info do
{
name: user_info.name,
email: user_info.email,
nickname: user_info.preferred_username,
first_name: user_info.given_name,
last_name: user_info.family_name,
gender: user_info.gender,
image: user_info.picture,
phone: user_info.phone_number,
urls: { website: user_info.website }
name: user_info.name,
email: user_info.email,
nickname: user_info.preferred_username,
first_name: user_info.given_name,
last_name: user_info.family_name,
gender: user_info.gender,
image: user_info.picture,
phone: user_info.phone_number,
urls: {website: user_info.website}
}
end

extra do
{ raw_info: user_info.raw_attributes }
{raw_info: user_info.raw_attributes}
end

credentials do
{
token: access_token.access_token,
refresh_token: access_token.refresh_token,
expires_in: access_token.expires_in,
scope: access_token.scope
id_token: access_token.id_token,
token: access_token.access_token,
refresh_token: access_token.refresh_token,
expires_in: access_token.expires_in,
scope: access_token.scope
}
end

def client
@client ||= ::OpenIDConnect::Client.new(client_options)
end

def config
@config ||= ::OpenIDConnect::Discovery::Provider::Config.discover!(options.issuer)
end

def request_phase
options.issuer = issuer if options.issuer.blank?
discover! if options.discovery
redirect authorize_uri
end

def callback_phase
client.redirect_uri = client_options.redirect_uri
client.authorization_code = authorization_code
access_token
super
error = request.params['error_reason'] || request.params['error']
if error
raise CallbackError.new(request.params['error'], request.params['error_description'] || request.params['error_reason'], request.params['error_uri'])
elsif request.params['state'].to_s.empty? || request.params['state'] != stored_state
raise CallbackError.new(:csrf_detected, 'CSRF detected')
else
options.issuer = issuer if options.issuer.blank?
discover! if options.discovery
client.redirect_uri = client_options.redirect_uri
client.authorization_code = authorization_code
access_token
super
end
rescue CallbackError => e
fail!(:invalid_credentials, e)
rescue ::Timeout::Error, ::Errno::ETIMEDOUT => e
fail!(:timeout, e)
rescue ::SocketError => e
fail!(:failed_to_connect, e)
end


def authorization_code
request.params["code"]
end

def authorize_uri
client.redirect_uri = client_options.redirect_uri
client.authorization_uri(
response_type: options.response_type,
scope: options.scope,
nonce: nonce,
response_type: options.response_type,
scope: options.scope,
state: new_state,
nonce: new_nonce,
)
end

def public_key
if options.discover
config.public_keys.first
else
key_or_secret
end
end

private

def issuer
resource = "#{client_options.scheme}://#{client_options.host}" + ((client_options.port) ? ":#{client_options.port.to_s}" : '')
::OpenIDConnect::Discovery::Provider.discover!(resource).issuer
end

def discover!
client_options.authorization_endpoint = config.authorization_endpoint
client_options.token_endpoint = config.token_endpoint
client_options.userinfo_endpoint = config.userinfo_endpoint
client_options.jwks_uri = config.jwks_uri
end

def user_info
@user_info ||= access_token.userinfo!
end

def access_token
@access_token ||= client.access_token!(scope: options.scope)
@access_token ||= lambda {
_access_token = client.access_token!
_id_token = decode_id_token _access_token.id_token
_id_token.verify!(
issuer: options.issuer,
client_id: client_options.identifier,
nonce: stored_nonce
)
_access_token
}.call()
end

def decode_id_token(id_token)
::OpenIDConnect::ResponseObject::IdToken.decode(id_token, public_key)
end


def client_options
options.client_options
end

def nonce
session[:nonce] = SecureRandom.hex(16)
def new_state
session['omniauth.state'] = SecureRandom.hex(16)
end

def stored_state
session.delete('omniauth.state')
end

def new_nonce
session['omniauth.nonce'] = SecureRandom.hex(16)
end

def stored_nonce
session.delete('omniauth.nonce')
end

def session
@env.nil? ? {} : super
end

def key_or_secret
case options.client_signing_alg
when :HS256, :HS384, :HS512
return client_options.secret
when :RS256, :RS384, :RS512
if options.client_jwk_signing_key
return parse_jwk_key(options.client_jwk_signing_key)
elsif options.client_x509_signing_key
return parse_x509_key(options.client_x509_signing_key)
end
else
end
end

def parse_x509_key(key)
OpenSSL::X509::Certificate.new key
end

def parse_jwk_key(key)
json = JSON.parse(key)
jwk = json['keys'].first
create_rsa_key(jwk['n'], jwk['e'])
end

def create_rsa_key(mod, exp)
key = OpenSSL::PKey::RSA.new
exponent = OpenSSL::BN.new decode(exp)
modulus = OpenSSL::BN.new decode(mod)
key.e = exponent
key.n = modulus
key
end

def decode(str)
UrlSafeBase64.decode64(str).unpack('B*').first.to_i(2).to_s
end

class CallbackError < StandardError
attr_accessor :error, :error_reason, :error_uri

def initialize(error, error_reason=nil, error_uri=nil)
self.error = error
self.error_reason = error_reason
self.error_uri = error_uri
end
end
end
end
end
Expand Down

0 comments on commit 9afd086

Please sign in to comment.