Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #25809 - JWT auth for external users
- Loading branch information
1 parent
6dd32db
commit f0ae0d6
Showing
18 changed files
with
381 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
class OidcJwt < JwtToken | ||
def decode | ||
return if token.blank? | ||
OidcJwtValidate.new(token).decoded_payload | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
class OidcJwtValidate | ||
attr_reader :decoded_token | ||
delegate :logger, to: :Rails | ||
|
||
def initialize(jwt_token) | ||
@jwt_token = jwt_token | ||
end | ||
|
||
def decoded_payload | ||
# OpenSSL#set_key method does not support ruby version < 2.4.0, apparently the JWT gem uses | ||
# OpenSSL#set_key method for all ruby version. We must remove this condition once new version | ||
# of the JWT(2.2.2) is released. | ||
unless OpenSSL::PKey::RSA.new.respond_to?(:set_key) | ||
Foreman::Logging.logger('app').error "SSO feature is not available for Ruby < 2.4.0" | ||
return nil | ||
end | ||
JWT.decode(@jwt_token, nil, true, | ||
{ aud: Setting['oidc_audience'], | ||
verify_aud: true, | ||
iss: Setting['oidc_issuer'], | ||
verify_iss: true, | ||
algorithms: [Setting['oidc_algorithm']], | ||
jwks: jwks_loader } | ||
).first | ||
rescue JWT::DecodeError => e | ||
Foreman::Logging.exception('Failed to decode JWT', e) | ||
nil | ||
end | ||
|
||
private | ||
|
||
def jwks_loader(options = {}) | ||
response = RestClient::Request.execute( | ||
:url => Setting['oidc_jwks_url'], | ||
:method => :get, | ||
:verify_ssl => true | ||
) | ||
json_response = JSON.parse(response) | ||
if json_response.is_a?(Hash) | ||
jwks_keys = json_response['keys'] | ||
@cached_keys = nil if options[:invalidate] # need to reload the keys | ||
@cached_keys ||= { keys: jwks_keys.map(&:symbolize_keys) } | ||
else | ||
raise JSON::ParserError.new('Invalid JWKS response') | ||
end | ||
rescue RestClient::Exception, SocketError, JSON::ParserError => e | ||
Foreman::Logging.exception('Failed to load the JWKS', e) | ||
{} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
module SSO | ||
class OpenidConnect < Base | ||
delegate :session, :to => :controller | ||
attr_reader :current_user | ||
|
||
def available? | ||
controller.api_request? && bearer_token_set? && valid_issuer? | ||
end | ||
|
||
def authenticate! | ||
payload = jwt_token.decode | ||
return nil if payload.nil? | ||
user = find_or_create_user_from_jwt(payload) | ||
@current_user = user | ||
update_session(payload) | ||
user&.login | ||
end | ||
|
||
def authenticated? | ||
self.user = User.current.presence || authenticate! | ||
end | ||
|
||
private | ||
|
||
def jwt_token | ||
@jwt_token ||= jwt_token_from_request | ||
end | ||
|
||
def jwt_token_from_request | ||
token = request.authorization.split(' ')[1] | ||
OidcJwt.new(token) | ||
end | ||
|
||
def bearer_token_set? | ||
request.authorization.present? && request.authorization.start_with?('Bearer') | ||
end | ||
|
||
def valid_issuer? | ||
payload = jwt_token.decoded_payload | ||
payload.key?('iss') && (payload['iss'] == Setting['oidc_issuer']) | ||
end | ||
|
||
def update_session(payload) | ||
session[:sso_method] = self.class.to_s | ||
session[:expires_at] = payload['exp'] | ||
end | ||
|
||
def find_or_create_user_from_jwt(payload) | ||
User.find_or_create_external_user( | ||
{ login: payload['preferred_username'], | ||
mail: payload['email'], | ||
firstname: payload['given_name'], | ||
lastname: payload['family_name']}, | ||
Setting['authorize_login_delegation_auth_source_user_autocreate'] | ||
) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.