Skip to content

Commit

Permalink
connect login.gov oidc configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
stepchud committed Jul 13, 2024
1 parent 3b8f6cf commit 34f195e
Show file tree
Hide file tree
Showing 12 changed files with 196 additions and 24 deletions.
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ gem "sqlite3", "~> 1.4"
# Use the Puma web server [https://github.com/puma/puma]
gem "puma", ">= 5.0"

# Use the popular Faraday HTTP library
gem "faraday"

# Use the JWT gem for JSON Web Tokens
gem "jwt"

# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
gem "importmap-rails"

Expand Down
37 changes: 13 additions & 24 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ GEM
tzinfo (~> 2.0)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
base64 (0.2.0)
bigdecimal (3.1.8)
bindex (0.8.1)
Expand All @@ -103,6 +102,11 @@ GEM
diff-lcs (1.5.1)
drb (2.2.1)
erubi (1.13.0)
faraday (2.10.0)
faraday-net_http (>= 2.0, < 3.2)
logger
faraday-net_http (3.1.0)
net-http
globalid (1.2.1)
activesupport (>= 6.1)
i18n (1.14.5)
Expand All @@ -118,8 +122,9 @@ GEM
jbuilder (2.12.0)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
json (2.7.2)
language_server-protocol (3.17.0.3)
jwt (2.8.2)
base64
logger (1.6.0)
loofah (2.22.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
Expand All @@ -134,6 +139,8 @@ GEM
minitest (5.23.1)
msgpack (1.7.2)
mutex_m (0.2.0)
net-http (0.4.1)
uri
net-imap (0.4.12)
date
net-protocol
Expand All @@ -152,10 +159,6 @@ GEM
racc (~> 1.4)
nokogiri (1.16.6-x86_64-linux)
racc (~> 1.4)
parallel (1.25.1)
parser (3.3.3.0)
ast (~> 2.4.1)
racc
psych (5.1.2)
stringio
public_suffix (5.0.5)
Expand Down Expand Up @@ -199,7 +202,6 @@ GEM
rake (>= 12.2)
thor (~> 1.0, >= 1.2.2)
zeitwerk (~> 2.6)
rainbow (3.1.1)
rake (13.2.1)
rdoc (6.7.0)
psych (>= 4.0.0)
Expand All @@ -225,20 +227,6 @@ GEM
rspec-mocks (~> 3.13)
rspec-support (~> 3.13)
rspec-support (3.13.1)
rubocop (1.64.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.31.3)
parser (>= 3.3.1.0)
ruby-progressbar (1.13.0)
rubyzip (2.3.2)
selenium-webdriver (4.21.1)
base64 (~> 0.2)
Expand Down Expand Up @@ -268,7 +256,7 @@ GEM
railties (>= 6.0.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (2.5.0)
uri (0.13.0)
web-console (4.2.1)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
Expand All @@ -293,12 +281,13 @@ DEPENDENCIES
bootsnap
capybara
debug
faraday
importmap-rails
jbuilder
jwt
puma (>= 5.0)
rails (~> 7.1.3, >= 7.1.3.4)
rspec-rails
rubocop
selenium-webdriver
sprockets-rails
sqlite3 (~> 1.4)
Expand Down
60 changes: 60 additions & 0 deletions app/controllers/sessions_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
class SessionsController < ApplicationController

before_action :handle_result_error, :well_known_configuration, :decode_token, only: [:result]

def new
# TODO: handle redirect to login page due to inactivity
end

def create
login_gov = LoginGov.new
redirect_to(login_gov.authorization_url, allow_other_host: true)
end

def delete
login_gov = LoginGov.new
# TODO: update user session status, clear out JWT
# TODO: add session duration to the security log
# TODO: delete session locally and Phoenix
redirect_to(login_gov.logout_url)
end

def result

end

private

def handle_result_error
if params[:error]
Rails.logger.error("Login.gov authentication error: #{params[:error]}")
flash[:error] = "There was an issue with logging in. Please try again."
redirect_to new_session_path
end

if params[:code].nil? && params[:state].nil?
Rails.logger.error("Login.gov unknown error")
flash[:error] = "Please try again."
redirect_to new_session_path
end
end

def well_known_configuration
@login_gov = LoginGov.new
status, body = @login_gov.get_well_known_configuration

if status != 200
Rails.logger.error("well-known/openid-configuration error: code=#{status} - body:\n#{body}")
flash[:error] = "There was an issue logging in."
redirect_to new_session_path
return
end

Rails.logger.debug("well_known_config response body=#{body}")
@openid_config = body
end

def decode_token
private_key = @login_gov.private_key
end
end
2 changes: 2 additions & 0 deletions app/helpers/sessions_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
module SessionsHelper
end
57 changes: 57 additions & 0 deletions app/models/login_gov.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
class LoginGov

# :acr_value,
# :client_id,
# :idp_host,
# :login_redirect_uri,
# :logout_redirect_uri,
# :logout_uri,
# :private_key_password,
# :private_key_path,
# :token_endpoint,
attr_reader :config

def initialize(config = Rails.configuration.login_gov_oidc)
@config = config.freeze.dup
end

def authorization_url
query = {
client_id: config[:client_id],
response_type: "code",
acr_values: config[:acr_value],
scope: "openid email",
redirect_uri: config[:login_redirect_uri],
state: SecureRandom.hex(16),
nonce: SecureRandom.hex(16),
prompt: "select_account"
}.to_query

"#{config[:idp_host]}/openid_connect/authorize?#{query}"
end

def logout_url
query = {
client_id: config[:client_id],
post_logout_redirect_uri: config[:logout_redirect_uri]
}.to_query

"#{config[:logout_uri]}?#{query}"
end

def well_known_configuration_url
config[:idp_host] + "/.well-known/openid-configuration"
end

def get_well_known_configuration
response = Faraday.get(well_known_configuration_url)
[response.status, response.body]
end

def private_key
key_path = config[:private_key_path]
key_pass = config[:private_key_password]
Rails.logger.info("Get private key from .pem file=#{key_path} pass=#{key_pass}")
"🔐"
end
end
2 changes: 2 additions & 0 deletions app/views/sessions/create.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Session#create</h1>
<p>Find me in app/views/session/create.html.erb</p>
2 changes: 2 additions & 0 deletions app/views/sessions/delete.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Session#delete</h1>
<p>Find me in app/views/session/delete.html.erb</p>
14 changes: 14 additions & 0 deletions app/views/sessions/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<div class="usa-card__container custom-card col-md-6 rules-of-behavior">
<div class="usa-card__body">
<div class="rules-wrapper">
<h2 class="text-center">You must accept the U.S. Government System terms to sign into this website</h2>
<p class="text-center rules-text">
This is a U.S. General Services Administration Federal Government
computer system that is "FOR OFFICIAL USE ONLY." This System is subject
to monitoring. Individuals found performing unauthorized activities are
subject to disciplinary action including criminal prosecution.
</p>
<%= button_to("Accept and Sign-in", session_path, class: "usa-button center-btn") %>
</div>
</div>
</div>
2 changes: 2 additions & 0 deletions app/views/sessions/result.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<h1>Session#result</h1>
<p>Find me in app/views/session/result.html.erb</p>
13 changes: 13 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,17 @@

# Raise error when a before_action's only/except options reference missing actions
config.action_controller.raise_on_missing_callback_actions = true

config.login_gov_oidc = {
idp_host: "https://idp.int.identitysandbox.gov",
login_redirect_uri: "http://localhost:3000/auth/result",
logout_uri: "https://idp.int.identitysandbox.gov/openid_connect/logout",
logout_redirect_uri: "https://www.challenge.gov/",
acr_value: "http://idmanagement.gov/ns/assurance/loa/1",
client_id: "urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:challenge_gov_platform_dev",
private_key_password: nil,
private_key_path: "config/private.pem",
public_key_path: "config/public.crt",
token_endpoint: "https://idp.int.identitysandbox.gov/api/openid_connect/token"
}
end
2 changes: 2 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
Rails.application.routes.draw do
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
get 'auth/result', to: 'sessions#result'
resource 'session', only: [:new, :create, :destroy]

# Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
# Can be used by load balancers and uptime monitors to verify that the app is live.
Expand Down
23 changes: 23 additions & 0 deletions test/controllers/sessions_controller_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require "test_helper"

class SessionsControllerTest < ActionDispatch::IntegrationTest
test "should get new" do
get session_new_url
assert_response :success
end

test "should get create" do
post session_create_url
assert_response :success
end

test "should get delete" do
get session_delete_url
assert_response :success
end

test "should get result" do
get session_result_url
assert_response :success
end
end

0 comments on commit 34f195e

Please sign in to comment.