├── .gitignore
├── .ruby-version
├── Gemfile
├── Gemfile.lock
├── Procfile
├── README.md
├── Rakefile
├── app
├── assets
│ ├── config
│ │ └── manifest.js
│ ├── images
│ │ └── .keep
│ ├── javascripts
│ │ ├── application.js
│ │ ├── auth.coffee
│ │ ├── cable.js
│ │ ├── channels
│ │ │ └── .keep
│ │ ├── home.coffee
│ │ └── sessions.coffee
│ └── stylesheets
│ │ ├── application.css
│ │ ├── auth.scss
│ │ ├── home.scss
│ │ └── sessions.scss
├── channels
│ └── application_cable
│ │ ├── channel.rb
│ │ └── connection.rb
├── controllers
│ ├── application_controller.rb
│ ├── auth_controller.rb
│ ├── concerns
│ │ └── .keep
│ ├── home_controller.rb
│ └── sessions_controller.rb
├── helpers
│ ├── application_helper.rb
│ ├── auth_helper.rb
│ ├── home_helper.rb
│ └── sessions_helper.rb
├── jobs
│ └── application_job.rb
├── mailers
│ └── application_mailer.rb
├── models
│ ├── application_record.rb
│ ├── cognito_session.rb
│ ├── concerns
│ │ └── .keep
│ └── user.rb
└── views
│ ├── home
│ ├── _user.html.erb
│ └── index.html.erb
│ └── layouts
│ ├── application.html.erb
│ ├── mailer.html.erb
│ └── mailer.text.erb
├── bin
├── bundle
├── rails
├── rake
├── setup
├── spring
├── update
└── yarn
├── config.ru
├── config
├── application.rb
├── boot.rb
├── cable.yml
├── credentials.yml.enc
├── database.yml
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── initializers
│ ├── application_controller_renderer.rb
│ ├── assets.rb
│ ├── backtrace_silencers.rb
│ ├── cognito.rb
│ ├── content_security_policy.rb
│ ├── cookies_serializer.rb
│ ├── filter_parameter_logging.rb
│ ├── inflections.rb
│ ├── mime_types.rb
│ ├── session_store.rb
│ └── wrap_parameters.rb
├── locales
│ └── en.yml
├── puma.rb
├── routes.rb
├── spring.rb
└── storage.yml
├── contrib
└── screenshots
│ ├── screenshot_1.png
│ ├── screenshot_2.png
│ ├── screenshot_3.png
│ ├── screenshot_4.png
│ ├── screenshot_5.png
│ └── screenshot_6.png
├── db
├── migrate
│ ├── 20190724144217_create_users.rb
│ ├── 20190724144310_create_cognito_sessions.rb
│ └── 20190724144814_add_sessions_table.rb
├── schema.rb
└── seeds.rb
├── lib
├── assets
│ └── .keep
├── cognito_client.rb
├── cognito_jwt_keys.rb
├── cognito_pool_tokens.rb
├── cognito_urls.rb
└── tasks
│ └── .keep
├── log
└── .keep
├── package.json
├── public
├── 404.html
├── 422.html
├── 500.html
├── apple-touch-icon-precomposed.png
├── apple-touch-icon.png
├── favicon.ico
└── robots.txt
├── storage
└── .keep
├── test
├── application_system_test_case.rb
├── controllers
│ ├── .keep
│ ├── auth_controller_test.rb
│ ├── home_controller_test.rb
│ └── sessions_controller_test.rb
├── fixtures
│ ├── .keep
│ ├── cognito_sessions.yml
│ ├── files
│ │ └── .keep
│ └── users.yml
├── helpers
│ └── .keep
├── integration
│ └── .keep
├── mailers
│ └── .keep
├── models
│ ├── .keep
│ ├── cognito_session_test.rb
│ └── user_test.rb
├── system
│ └── .keep
└── test_helper.rb
├── tmp
└── .keep
└── vendor
└── .keep
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore the default SQLite database.
11 | /db/*.sqlite3
12 | /db/*.sqlite3-journal
13 |
14 | # Ignore all logfiles and tempfiles.
15 | /log/*
16 | /tmp/*
17 | !/log/.keep
18 | !/tmp/.keep
19 |
20 | # Ignore uploaded files in development
21 | /storage/*
22 | !/storage/.keep
23 |
24 | /node_modules
25 | /yarn-error.log
26 |
27 | /public/assets
28 | .byebug_history
29 |
30 | # Ignore master key for decrypting credentials and more.
31 | /config/master.key
32 |
--------------------------------------------------------------------------------
/.ruby-version:
--------------------------------------------------------------------------------
1 | ruby-2.6.3
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | ruby '2.6.3'
5 |
6 | gem 'activerecord-session_store', '~> 2.0.0'
7 | gem 'excon', '~> 0.71.0'
8 | gem 'json-jwt', '~> 1.11.0'
9 |
10 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
11 | gem 'rails', '~> 5.2.4'
12 | # Use Puma as the app server
13 | gem 'puma', '~> 5.6'
14 | # Use SCSS for stylesheets
15 | gem 'sass-rails', '~> 5.0'
16 | # Use Uglifier as compressor for JavaScript assets
17 | gem 'uglifier', '>= 1.3.0'
18 | # See https://github.com/rails/execjs#readme for more supported runtimes
19 | # gem 'mini_racer', platforms: :ruby
20 |
21 | # Use CoffeeScript for .coffee assets and views
22 | gem 'coffee-rails', '~> 4.2'
23 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
24 | gem 'turbolinks', '~> 5'
25 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
26 | gem 'jbuilder', '~> 2.5'
27 | # Use Redis adapter to run Action Cable in production
28 | # gem 'redis', '~> 4.0'
29 | # Use ActiveModel has_secure_password
30 | # gem 'bcrypt', '~> 3.1.7'
31 |
32 | # Use ActiveStorage variant
33 | # gem 'mini_magick', '~> 4.8'
34 |
35 | # Use Capistrano for deployment
36 | # gem 'capistrano-rails', group: :development
37 |
38 | # Reduces boot times through caching; required in config/boot.rb
39 | gem 'bootsnap', '>= 1.1.0', require: false
40 |
41 | # CVE-2019-5477
42 | gem 'nokogiri', '>= 1.10.4'
43 |
44 | group :production do
45 | gem 'pg', '1.1.4'
46 | end
47 |
48 | group :development, :test do
49 | # Use sqlite3 as the database for Active Record
50 | gem 'sqlite3'
51 |
52 | gem 'dotenv-rails', '~> 2.7.4'
53 |
54 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
55 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
56 | end
57 |
58 | group :development do
59 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
60 | gem 'web-console', '>= 3.3.0'
61 | gem 'listen', '>= 3.0.5', '< 3.2'
62 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
63 | gem 'spring'
64 | gem 'spring-watcher-listen', '~> 2.0.0'
65 | end
66 |
67 | group :test do
68 | # Adds support for Capybara system testing and selenium driver
69 | gem 'capybara', '>= 2.15'
70 | gem 'selenium-webdriver'
71 | # Easy installation and use of chromedriver to run system tests with Chrome
72 | gem 'chromedriver-helper'
73 | end
74 |
75 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
76 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
77 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | actioncable (5.2.4.3)
5 | actionpack (= 5.2.4.3)
6 | nio4r (~> 2.0)
7 | websocket-driver (>= 0.6.1)
8 | actionmailer (5.2.4.3)
9 | actionpack (= 5.2.4.3)
10 | actionview (= 5.2.4.3)
11 | activejob (= 5.2.4.3)
12 | mail (~> 2.5, >= 2.5.4)
13 | rails-dom-testing (~> 2.0)
14 | actionpack (5.2.4.3)
15 | actionview (= 5.2.4.3)
16 | activesupport (= 5.2.4.3)
17 | rack (~> 2.0, >= 2.0.8)
18 | rack-test (>= 0.6.3)
19 | rails-dom-testing (~> 2.0)
20 | rails-html-sanitizer (~> 1.0, >= 1.0.2)
21 | actionview (5.2.4.3)
22 | activesupport (= 5.2.4.3)
23 | builder (~> 3.1)
24 | erubi (~> 1.4)
25 | rails-dom-testing (~> 2.0)
26 | rails-html-sanitizer (~> 1.0, >= 1.0.3)
27 | activejob (5.2.4.3)
28 | activesupport (= 5.2.4.3)
29 | globalid (>= 0.3.6)
30 | activemodel (5.2.4.3)
31 | activesupport (= 5.2.4.3)
32 | activerecord (5.2.4.3)
33 | activemodel (= 5.2.4.3)
34 | activesupport (= 5.2.4.3)
35 | arel (>= 9.0)
36 | activerecord-session_store (2.0.0)
37 | actionpack (>= 5.2.4.1)
38 | activerecord (>= 5.2.4.1)
39 | multi_json (~> 1.11, >= 1.11.2)
40 | rack (>= 2.0.8, < 3)
41 | railties (>= 5.2.4.1)
42 | activestorage (5.2.4.3)
43 | actionpack (= 5.2.4.3)
44 | activerecord (= 5.2.4.3)
45 | marcel (~> 0.3.1)
46 | activesupport (5.2.4.3)
47 | concurrent-ruby (~> 1.0, >= 1.0.2)
48 | i18n (>= 0.7, < 2)
49 | minitest (~> 5.1)
50 | tzinfo (~> 1.1)
51 | addressable (2.8.0)
52 | public_suffix (>= 2.0.2, < 5.0)
53 | aes_key_wrap (1.0.1)
54 | archive-zip (0.12.0)
55 | io-like (~> 0.3.0)
56 | arel (9.0.0)
57 | bindata (2.4.10)
58 | bindex (0.8.1)
59 | bootsnap (1.4.4)
60 | msgpack (~> 1.0)
61 | builder (3.2.4)
62 | byebug (11.0.1)
63 | capybara (3.26.0)
64 | addressable
65 | mini_mime (>= 0.1.3)
66 | nokogiri (~> 1.8)
67 | rack (>= 1.6.0)
68 | rack-test (>= 0.6.3)
69 | regexp_parser (~> 1.5)
70 | xpath (~> 3.2)
71 | childprocess (1.0.1)
72 | rake (< 13.0)
73 | chromedriver-helper (2.1.1)
74 | archive-zip (~> 0.10)
75 | nokogiri (~> 1.8)
76 | coffee-rails (4.2.2)
77 | coffee-script (>= 2.2.0)
78 | railties (>= 4.0.0)
79 | coffee-script (2.4.1)
80 | coffee-script-source
81 | execjs
82 | coffee-script-source (1.12.2)
83 | concurrent-ruby (1.1.10)
84 | crass (1.0.6)
85 | dotenv (2.7.4)
86 | dotenv-rails (2.7.4)
87 | dotenv (= 2.7.4)
88 | railties (>= 3.2, < 6.1)
89 | erubi (1.10.0)
90 | excon (0.71.0)
91 | execjs (2.7.0)
92 | ffi (1.11.1)
93 | globalid (1.0.1)
94 | activesupport (>= 5.0)
95 | i18n (1.12.0)
96 | concurrent-ruby (~> 1.0)
97 | io-like (0.3.0)
98 | jbuilder (2.9.1)
99 | activesupport (>= 4.2.0)
100 | json-jwt (1.11.0)
101 | activesupport (>= 4.2)
102 | aes_key_wrap
103 | bindata
104 | listen (3.1.5)
105 | rb-fsevent (~> 0.9, >= 0.9.4)
106 | rb-inotify (~> 0.9, >= 0.9.7)
107 | ruby_dep (~> 1.2)
108 | loofah (2.19.1)
109 | crass (~> 1.0.2)
110 | nokogiri (>= 1.5.9)
111 | mail (2.7.1)
112 | mini_mime (>= 0.1.1)
113 | marcel (0.3.3)
114 | mimemagic (~> 0.3.2)
115 | method_source (1.0.0)
116 | mimemagic (0.3.10)
117 | nokogiri (~> 1)
118 | rake
119 | mini_mime (1.0.2)
120 | mini_portile2 (2.8.0)
121 | minitest (5.17.0)
122 | msgpack (1.3.0)
123 | multi_json (1.15.0)
124 | nio4r (2.5.9)
125 | nokogiri (1.13.10)
126 | mini_portile2 (~> 2.8.0)
127 | racc (~> 1.4)
128 | pg (1.1.4)
129 | public_suffix (4.0.6)
130 | puma (5.6.7)
131 | nio4r (~> 2.0)
132 | racc (1.6.1)
133 | rack (2.2.6.4)
134 | rack-test (1.1.0)
135 | rack (>= 1.0, < 3)
136 | rails (5.2.4.3)
137 | actioncable (= 5.2.4.3)
138 | actionmailer (= 5.2.4.3)
139 | actionpack (= 5.2.4.3)
140 | actionview (= 5.2.4.3)
141 | activejob (= 5.2.4.3)
142 | activemodel (= 5.2.4.3)
143 | activerecord (= 5.2.4.3)
144 | activestorage (= 5.2.4.3)
145 | activesupport (= 5.2.4.3)
146 | bundler (>= 1.3.0)
147 | railties (= 5.2.4.3)
148 | sprockets-rails (>= 2.0.0)
149 | rails-dom-testing (2.0.3)
150 | activesupport (>= 4.2.0)
151 | nokogiri (>= 1.6)
152 | rails-html-sanitizer (1.4.4)
153 | loofah (~> 2.19, >= 2.19.1)
154 | railties (5.2.4.3)
155 | actionpack (= 5.2.4.3)
156 | activesupport (= 5.2.4.3)
157 | method_source
158 | rake (>= 0.8.7)
159 | thor (>= 0.19.0, < 2.0)
160 | rake (12.3.3)
161 | rb-fsevent (0.10.3)
162 | rb-inotify (0.10.0)
163 | ffi (~> 1.0)
164 | regexp_parser (1.6.0)
165 | ruby_dep (1.5.0)
166 | rubyzip (1.3.0)
167 | sass (3.7.4)
168 | sass-listen (~> 4.0.0)
169 | sass-listen (4.0.0)
170 | rb-fsevent (~> 0.9, >= 0.9.4)
171 | rb-inotify (~> 0.9, >= 0.9.7)
172 | sass-rails (5.0.7)
173 | railties (>= 4.0.0, < 6)
174 | sass (~> 3.1)
175 | sprockets (>= 2.8, < 4.0)
176 | sprockets-rails (>= 2.0, < 4.0)
177 | tilt (>= 1.1, < 3)
178 | selenium-webdriver (3.142.3)
179 | childprocess (>= 0.5, < 2.0)
180 | rubyzip (~> 1.2, >= 1.2.2)
181 | spring (2.1.0)
182 | spring-watcher-listen (2.0.1)
183 | listen (>= 2.7, < 4.0)
184 | spring (>= 1.2, < 3.0)
185 | sprockets (3.7.2)
186 | concurrent-ruby (~> 1.0)
187 | rack (> 1, < 3)
188 | sprockets-rails (3.2.1)
189 | actionpack (>= 4.0)
190 | activesupport (>= 4.0)
191 | sprockets (>= 3.0.0)
192 | sqlite3 (1.4.1)
193 | thor (1.1.0)
194 | thread_safe (0.3.6)
195 | tilt (2.0.9)
196 | turbolinks (5.2.0)
197 | turbolinks-source (~> 5.2)
198 | turbolinks-source (5.2.0)
199 | tzinfo (1.2.10)
200 | thread_safe (~> 0.1)
201 | uglifier (4.1.20)
202 | execjs (>= 0.3.0, < 3)
203 | web-console (3.7.0)
204 | actionview (>= 5.0)
205 | activemodel (>= 5.0)
206 | bindex (>= 0.4.0)
207 | railties (>= 5.0)
208 | websocket-driver (0.7.2)
209 | websocket-extensions (>= 0.1.0)
210 | websocket-extensions (0.1.5)
211 | xpath (3.2.0)
212 | nokogiri (~> 1.8)
213 |
214 | PLATFORMS
215 | ruby
216 |
217 | DEPENDENCIES
218 | activerecord-session_store (~> 2.0.0)
219 | bootsnap (>= 1.1.0)
220 | byebug
221 | capybara (>= 2.15)
222 | chromedriver-helper
223 | coffee-rails (~> 4.2)
224 | dotenv-rails (~> 2.7.4)
225 | excon (~> 0.71.0)
226 | jbuilder (~> 2.5)
227 | json-jwt (~> 1.11.0)
228 | listen (>= 3.0.5, < 3.2)
229 | nokogiri (>= 1.10.4)
230 | pg (= 1.1.4)
231 | puma (~> 5.6)
232 | rails (~> 5.2.4)
233 | sass-rails (~> 5.0)
234 | selenium-webdriver
235 | spring
236 | spring-watcher-listen (~> 2.0.0)
237 | sqlite3
238 | turbolinks (~> 5)
239 | tzinfo-data
240 | uglifier (>= 1.3.0)
241 | web-console (>= 3.3.0)
242 |
243 | RUBY VERSION
244 | ruby 2.6.3p62
245 |
246 | BUNDLED WITH
247 | 1.17.2
248 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec puma -t 5:5 -p ${PORT:-3000} -e ${RACK_ENV:-development}
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rails AWS Cognito User Pool Example
2 |
3 | This is a Rails application that demonstrates an AWS Cognito User Pool
4 | server-side authentication flow using the Cognito Hosted UI. This was
5 | extracted from another application that leveraged the Cognito User
6 | Pools for minimal user authentication management.
7 |
8 | User Sign Up, Sign In, and Sign Out are handled directly with Cognito
9 | and the [Hosted
10 | UI](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-app-integration.html)
11 | auth forms. The Rails application simply extracts the authentication
12 | data from the redirect after an authentication action is
13 | performed. Server-side session keys track the Cognito tokens and
14 | automatically refresh expired tokens.
15 |
16 | There's a demo of this application running at
17 | [https://cognito-rails-example.herokuapp.com/](https://cognito-rails-example.herokuapp.com/).
18 |
19 | # Setup
20 |
21 | ## Create a Cognito User Pool
22 |
23 | Create a new Cognito User Pool in the AWS console. The following
24 | screenshots show the non-default settings I used to create this
25 | pool. This is just a minimal example, so you will want to change the
26 | settings to match your preferences.
27 |
28 | 
29 |
30 | Password policy for this pool is minimally restrictive, match your security org requirements.
31 |
32 | 
33 |
34 | We want minimal friction in our sign up form, so we will only collect
35 | an email + password for now. You can include any number of attributes
36 | in the sign up form. The email will act as the username in our setup.
37 |
38 | 
39 |
40 | We need at least one app client for our web app to talk to the
41 | API. We'll need to lookup auth codes so enable 'Generate client
42 | secret'. The name doesn't really matter so pick anything.
43 |
44 | 
45 |
46 | With the app client created, you need to set the app client
47 | settings. Set the callback and sign out URLs to match your
48 | application. These URLs must match the same redirect URLs used in the
49 | application. Also, ensure the *authorization code grant* is selected
50 | and *email* is in the allowed oauth scopes.
51 |
52 | 
53 |
54 | Finally, after the pool is created we will set the domain name that is
55 | used for the hosted UI. If this were a production application you
56 | would probably want to use your own full domain name instead.
57 |
58 | 
59 |
60 | ## Configure the app
61 |
62 | These are the required environment variables for this app:
63 |
64 | * `AWS_COGNITO_APP_CLIENT_ID`
65 | * `AWS_COGNITO_APP_CLIENT_SECRET`
66 | * `AWS_COGNITO_DOMAIN`
67 | * `AWS_COGNITO_POOL_ID`
68 | * `AWS_COGNITO_REGION`
69 |
70 | # TODO
71 |
72 | * Track a user session through signup/signin for funnel analytics.
73 | * Multiple identify providers (Facebook, Google, etc)
74 | * Expire old sessions
75 | [periodically](https://github.com/rails/activerecord-session_store#installation)
76 | * Refresh active Cognito sessions ahead of their expiration to avoid
77 | blocking on the next request
78 |
79 | # Alternatives
80 |
81 | The
82 | [omniauth-cognito-idp](https://github.com/Sage/omniauth-cognito-idp/blob/master/README.md)
83 | provides an alternative approach that leverages the
84 | [OmniAuth](https://github.com/omniauth/omniauth) framework to build a
85 | Cognito aware oauth2 middleware.
86 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative 'config/application'
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/app/assets/images/.keep
--------------------------------------------------------------------------------
/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
5 | // vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require rails-ujs
14 | //= require activestorage
15 | //= require turbolinks
16 | //= require_tree .
17 |
--------------------------------------------------------------------------------
/app/assets/javascripts/auth.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/cable.js:
--------------------------------------------------------------------------------
1 | // Action Cable provides the framework to deal with WebSockets in Rails.
2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3 | //
4 | //= require action_cable
5 | //= require_self
6 | //= require_tree ./channels
7 |
8 | (function() {
9 | this.App || (this.App = {});
10 |
11 | App.cable = ActionCable.createConsumer();
12 |
13 | }).call(this);
14 |
--------------------------------------------------------------------------------
/app/assets/javascripts/channels/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/app/assets/javascripts/channels/.keep
--------------------------------------------------------------------------------
/app/assets/javascripts/home.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/app/assets/javascripts/sessions.coffee:
--------------------------------------------------------------------------------
1 | # Place all the behaviors and hooks related to the matching controller here.
2 | # All this logic will automatically be available in application.js.
3 | # You can use CoffeeScript in this file: http://coffeescript.org/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6 | * vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/auth.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the auth controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/home.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the home controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/assets/stylesheets/sessions.scss:
--------------------------------------------------------------------------------
1 | // Place all the styles related to the sessions controller here.
2 | // They will automatically be included in application.css.
3 | // You can use Sass (SCSS) here: http://sass-lang.com/
4 |
--------------------------------------------------------------------------------
/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | require 'cognito_jwt_keys'
2 | require 'cognito_client'
3 |
4 | class ApplicationController < ActionController::Base
5 | before_action :check_signed_in
6 |
7 | def check_signed_in
8 | @is_signed_in = false
9 | @current_user = nil
10 | @cognito_session = nil
11 |
12 | cognito_session = nil
13 | if session[:cognito_session_id]
14 | begin
15 | cognito_session = CognitoSession.find(session[:cognito_session_id])
16 | rescue ActiveRecord::RecordNotFound
17 | end
18 | end
19 |
20 | unless cognito_session
21 | return
22 | end
23 |
24 | now = Time.now.tv_sec
25 |
26 | if cognito_session.expire_time > now
27 | # Still valid, use
28 | #
29 |
30 | Rails.logger.info("Found a non-expired cognito session: #{cognito_session.id}")
31 | @is_signed_in = true
32 | @current_user = cognito_session.user
33 | @cognito_session = cognito_session
34 | return
35 | end
36 |
37 | Rails.logger.info("Refreshing cognito session: #{cognito_session.id}")
38 |
39 | # Need to refresh token
40 | if refresh_cognito_session(cognito_session)
41 | @is_signed_in = true
42 | @current_user = cognito_session.user
43 | @cognito_session = cognito_session
44 | return
45 | end
46 | end
47 |
48 | def refresh_cognito_session(cognito_session)
49 | client = new_cognito_client()
50 |
51 | resp = client.refresh_id_token(cognito_session.refresh_token)
52 |
53 | return false unless resp
54 |
55 | cognito_session.expire_time = resp.id_token[:exp]
56 | cognito_session.issued_time = resp.id_token[:auth_time]
57 | cognito_session.audience = resp.id_token[:aud]
58 |
59 | cognito_session.save!
60 | end
61 |
62 | def new_cognito_client
63 | CognitoClient.new(:redirect_uri => auth_sign_in_url)
64 | end
65 | end
66 |
--------------------------------------------------------------------------------
/app/controllers/auth_controller.rb:
--------------------------------------------------------------------------------
1 | class AuthController < ApplicationController
2 | def signin
3 | unless params[:code]
4 | render :nothing => true, :status => :bad_request
5 | return
6 | end
7 |
8 | resp = lookup_auth_code(params[:code])
9 | unless resp
10 | redirect_to '/'
11 | return
12 | end
13 |
14 | ActiveRecord::Base.transaction do
15 | user = User.where(subscriber: resp.id_token[:sub]).first
16 | if user.nil?
17 | user = User.create(subscriber: resp.id_token[:sub],
18 | email: resp.id_token[:email])
19 | end
20 |
21 | cognito_session = CognitoSession.create(user: user,
22 | expire_time: resp.id_token[:exp],
23 | issued_time: resp.id_token[:auth_time],
24 | audience: resp.id_token[:aud],
25 | refresh_token: resp.refresh_token)
26 | session[:cognito_session_id] = cognito_session.id
27 | end
28 |
29 | # Alternatively, you could redirect to a saved URL
30 | redirect_to '/'
31 | end
32 |
33 | def signout
34 | if cognito_session_id = session[:cognito_session_id]
35 | cognito_session = CognitoSession.find(cognito_session_id) rescue nil
36 | cognito_session.destroy if cognito_session
37 | session.delete(:cognito_session_id)
38 | end
39 |
40 | redirect_to '/'
41 | end
42 |
43 | def lookup_auth_code(code)
44 | client = new_cognito_client()
45 | client.get_pool_tokens(code)
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | end
3 |
--------------------------------------------------------------------------------
/app/controllers/sessions_controller.rb:
--------------------------------------------------------------------------------
1 | class SessionsController < ApplicationController
2 | def signin
3 | redirect_to cognito_signin_url
4 | end
5 |
6 | def signout
7 | redirect_to cognito_signout_url
8 | end
9 |
10 | def signup
11 | redirect_to cognito_signup_url
12 | end
13 |
14 | private
15 |
16 | def cognito_signin_url
17 | CognitoUrls.login_uri(ENV['AWS_COGNITO_APP_CLIENT_ID'],
18 | signin_redirect_uri)
19 | end
20 |
21 | def cognito_signup_url
22 | CognitoUrls.signup_uri(ENV['AWS_COGNITO_APP_CLIENT_ID'],
23 | signin_redirect_uri)
24 | end
25 |
26 | def cognito_signout_url
27 | CognitoUrls.logout_uri(ENV['AWS_COGNITO_APP_CLIENT_ID'],
28 | signout_redirect_uri)
29 | end
30 |
31 | def signin_redirect_uri
32 | auth_sign_in_url
33 | end
34 |
35 | def signout_redirect_uri
36 | auth_sign_out_url
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/auth_helper.rb:
--------------------------------------------------------------------------------
1 | module AuthHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/home_helper.rb:
--------------------------------------------------------------------------------
1 | module HomeHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/sessions_helper.rb:
--------------------------------------------------------------------------------
1 | module SessionsHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: 'from@example.com'
3 | layout 'mailer'
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/app/models/cognito_session.rb:
--------------------------------------------------------------------------------
1 | class CognitoSession < ApplicationRecord
2 | belongs_to :user
3 | end
4 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/models/user.rb:
--------------------------------------------------------------------------------
1 | class User < ApplicationRecord
2 | has_many :cognito_sessions
3 | end
4 |
--------------------------------------------------------------------------------
/app/views/home/_user.html.erb:
--------------------------------------------------------------------------------
1 |
Signed in
2 |
3 | - Email: <%= @current_user.email %>
4 | - ID Token Subject: <%= @current_user.subscriber %>
5 | - ID Token Issued: <%= Time.at(@cognito_session.issued_time).utc %>
6 | - ID Token Expiration: <%= Time.at(@cognito_session.expire_time).utc %>
7 |
8 |
--------------------------------------------------------------------------------
/app/views/home/index.html.erb:
--------------------------------------------------------------------------------
1 | <% if (!@is_signed_in) %>
2 | Please sign in.
3 | <% else %>
4 | <%= render 'user' %>
5 | <% end %>
6 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | RailsCognitoUserPoolExample
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 |
9 |
10 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
11 | <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 | <% if (!@is_signed_in) %>
22 |
Sign In
23 |
Sign Up
24 | <% else %>
25 |
Sign Out
26 | <% end %>
27 |
28 | <%= yield %>
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | APP_PATH = File.expand_path('../config/application', __dir__)
8 | require_relative '../config/boot'
9 | require 'rails/commands'
10 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | require_relative '../config/boot'
8 | require 'rake'
9 | Rake.application.run
10 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a starting point to setup your application.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | # puts "\n== Copying sample files =="
24 | # unless File.exist?('config/database.yml')
25 | # cp 'config/database.yml.sample', 'config/database.yml'
26 | # end
27 |
28 | puts "\n== Preparing database =="
29 | system! 'bin/rails db:setup'
30 |
31 | puts "\n== Removing old logs and tempfiles =="
32 | system! 'bin/rails log:clear tmp:clear'
33 |
34 | puts "\n== Restarting application server =="
35 | system! 'bin/rails restart'
36 | end
37 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads Spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
11 | spring = lockfile.specs.detect { |spec| spec.name == 'spring' }
12 | if spring
13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
14 | gem 'spring', spring.version
15 | require 'spring/binstub'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | puts "\n== Updating database =="
24 | system! 'bin/rails db:migrate'
25 |
26 | puts "\n== Removing old logs and tempfiles =="
27 | system! 'bin/rails log:clear tmp:clear'
28 |
29 | puts "\n== Restarting application server =="
30 | system! 'bin/rails restart'
31 | end
32 |
--------------------------------------------------------------------------------
/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | begin
5 | exec "yarnpkg", *ARGV
6 | rescue Errno::ENOENT
7 | $stderr.puts "Yarn executable was not detected in the system."
8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9 | exit 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative 'config/environment'
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | require 'rails/all'
4 |
5 | # Require the gems listed in Gemfile, including any gems
6 | # you've limited to :test, :development, or :production.
7 | Bundler.require(*Rails.groups)
8 |
9 | module RailsCognitoUserPoolExample
10 | class Application < Rails::Application
11 | # Initialize configuration defaults for originally generated Rails version.
12 | config.load_defaults 5.2
13 |
14 | # Settings in config/environments/* take precedence over those specified here.
15 | # Application configuration can go into files in config/initializers
16 | # -- all .rb files in that directory are automatically loaded after loading
17 | # the framework and any gems in your application.
18 |
19 | ActiveRecord::SessionStore::Session.serializer = :json
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: async
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: rails-cognito-user-pool-example_production
11 |
--------------------------------------------------------------------------------
/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | WOuZeOAVNLVib9Mr4yziqJ5edKFm7peBxpjl4UuOmC38u9yIfIEzqMHN/A4H2yux7lifqVKL9M94bLkI9ZTXth/EXTbnNAlfk+EQfcw9ort8v1bQWflnUrrgoNdaC9M8HK+BKTtXiareYn4RA+aYbHdb78LY6d+MbHTP+fJ6y7Ln6xpmNBlsv1UVgcTBq+pKNDsiQX4pG3fQnLGnqHnGLk6CXLV5R9+tXgsAjIuRkPp9A6IZLR7LLP6qhGI3wy+xbFKG9rPeXibLH+jUHidHdBkA9xXcrqz04ntEsX8i5RzantKRL3OTtR6bZH0LxYB55vtcuGwX9fUnKOh30pwQHmrBrVxc6lyWpii8TUNxJ3fdE/A2nTx+Jgw39uTOK9Hu6JCnHqlLn3zzJ9svxWLTN1L8owYbSW4l+l03--7/r+nJ9u1eVJlTr5--Vaw90kApoPwN2wfCnYwVPA==
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | # SQLite version 3.x
2 | # gem install sqlite3
3 | #
4 | # Ensure the SQLite 3 gem is defined in your Gemfile
5 | # gem 'sqlite3'
6 | #
7 | default: &default
8 | adapter: sqlite3
9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10 | timeout: 5000
11 |
12 | development:
13 | <<: *default
14 | database: db/development.sqlite3
15 |
16 | # Warning: The database defined as "test" will be erased and
17 | # re-generated from your development database when you run "rake".
18 | # Do not set this db to the same as development or production.
19 | test:
20 | <<: *default
21 | database: db/test.sqlite3
22 |
23 | production:
24 | <<: *default
25 | database: db/production.sqlite3
26 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # In the development environment your application's code is reloaded on
5 | # every request. This slows down response time but is perfect for development
6 | # since you don't have to restart the web server when you make code changes.
7 | config.cache_classes = false
8 |
9 | # Do not eager load code on boot.
10 | config.eager_load = false
11 |
12 | # Show full error reports.
13 | config.consider_all_requests_local = true
14 |
15 | # Enable/disable caching. By default caching is disabled.
16 | # Run rails dev:cache to toggle caching.
17 | if Rails.root.join('tmp', 'caching-dev.txt').exist?
18 | config.action_controller.perform_caching = true
19 |
20 | config.cache_store = :memory_store
21 | config.public_file_server.headers = {
22 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
23 | }
24 | else
25 | config.action_controller.perform_caching = false
26 |
27 | config.cache_store = :null_store
28 | end
29 |
30 | # Store uploaded files on the local file system (see config/storage.yml for options)
31 | config.active_storage.service = :local
32 |
33 | # Don't care if the mailer can't send.
34 | config.action_mailer.raise_delivery_errors = false
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | # Print deprecation notices to the Rails logger.
39 | config.active_support.deprecation = :log
40 |
41 | # Raise an error on page load if there are pending migrations.
42 | config.active_record.migration_error = :page_load
43 |
44 | # Highlight code that triggered database queries in logs.
45 | config.active_record.verbose_query_logs = true
46 |
47 | # Debug mode disables concatenation and preprocessing of assets.
48 | # This option may cause significant delays in view rendering with a large
49 | # number of complex assets.
50 | config.assets.debug = true
51 |
52 | # Suppress logger output for asset requests.
53 | config.assets.quiet = true
54 |
55 | # Raises error for missing translations
56 | # config.action_view.raise_on_missing_translations = true
57 |
58 | # Use an evented file watcher to asynchronously detect changes in source code,
59 | # routes, locales, etc. This feature depends on the listen gem.
60 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
61 | end
62 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
19 | # config.require_master_key = true
20 |
21 | # Disable serving static files from the `/public` folder by default since
22 | # Apache or NGINX already handles this.
23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
24 |
25 | # Compress JavaScripts and CSS.
26 | config.assets.js_compressor = :uglifier
27 | # config.assets.css_compressor = :sass
28 |
29 | # Do not fallback to assets pipeline if a precompiled asset is missed.
30 | config.assets.compile = false
31 |
32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
33 |
34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
35 | # config.action_controller.asset_host = 'http://assets.example.com'
36 |
37 | # Specifies the header that your server uses for sending files.
38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
40 |
41 | # Store uploaded files on the local file system (see config/storage.yml for options)
42 | config.active_storage.service = :local
43 |
44 | # Mount Action Cable outside main process or domain
45 | # config.action_cable.mount_path = nil
46 | # config.action_cable.url = 'wss://example.com/cable'
47 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
48 |
49 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
50 | # config.force_ssl = true
51 |
52 | # Use the lowest log level to ensure availability of diagnostic information
53 | # when problems arise.
54 | config.log_level = :debug
55 |
56 | # Prepend all log lines with the following tags.
57 | config.log_tags = [ :request_id ]
58 |
59 | # Use a different cache store in production.
60 | # config.cache_store = :mem_cache_store
61 |
62 | # Use a real queuing backend for Active Job (and separate queues per environment)
63 | # config.active_job.queue_adapter = :resque
64 | # config.active_job.queue_name_prefix = "rails-cognito-user-pool-example_#{Rails.env}"
65 |
66 | config.action_mailer.perform_caching = false
67 |
68 | # Ignore bad email addresses and do not raise email delivery errors.
69 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
70 | # config.action_mailer.raise_delivery_errors = false
71 |
72 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
73 | # the I18n.default_locale when a translation cannot be found).
74 | config.i18n.fallbacks = true
75 |
76 | # Send deprecation notices to registered listeners.
77 | config.active_support.deprecation = :notify
78 |
79 | # Use default logging formatter so that PID and timestamp are not suppressed.
80 | config.log_formatter = ::Logger::Formatter.new
81 |
82 | # Use a different logger for distributed setups.
83 | # require 'syslog/logger'
84 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
85 |
86 | if ENV["RAILS_LOG_TO_STDOUT"].present?
87 | logger = ActiveSupport::Logger.new(STDOUT)
88 | logger.formatter = config.log_formatter
89 | config.logger = ActiveSupport::TaggedLogging.new(logger)
90 | end
91 |
92 | # Do not dump schema after migrations.
93 | config.active_record.dump_schema_after_migration = false
94 | end
95 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # The test environment is used exclusively to run your application's
5 | # test suite. You never need to work with it otherwise. Remember that
6 | # your test database is "scratch space" for the test suite and is wiped
7 | # and recreated between test runs. Don't rely on the data there!
8 | config.cache_classes = true
9 |
10 | # Do not eager load code on boot. This avoids loading your whole application
11 | # just for the purpose of running a single test. If you are using a tool that
12 | # preloads Rails for running tests, you may have to set it to true.
13 | config.eager_load = false
14 |
15 | # Configure public file server for tests with Cache-Control for performance.
16 | config.public_file_server.enabled = true
17 | config.public_file_server.headers = {
18 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
19 | }
20 |
21 | # Show full error reports and disable caching.
22 | config.consider_all_requests_local = true
23 | config.action_controller.perform_caching = false
24 |
25 | # Raise exceptions instead of rendering exception templates.
26 | config.action_dispatch.show_exceptions = false
27 |
28 | # Disable request forgery protection in test environment.
29 | config.action_controller.allow_forgery_protection = false
30 |
31 | # Store uploaded files on the local file system in a temporary directory
32 | config.active_storage.service = :test
33 |
34 | config.action_mailer.perform_caching = false
35 |
36 | # Tell Action Mailer not to deliver emails to the real world.
37 | # The :test delivery method accumulates sent emails in the
38 | # ActionMailer::Base.deliveries array.
39 | config.action_mailer.delivery_method = :test
40 |
41 | # Print deprecation notices to the stderr.
42 | config.active_support.deprecation = :stderr
43 |
44 | # Raises error for missing translations
45 | # config.action_view.raise_on_missing_translations = true
46 | end
47 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/config/initializers/cognito.rb:
--------------------------------------------------------------------------------
1 | require 'cognito_jwt_keys'
2 | require 'cognito_urls'
3 |
4 | if !ENV['AWS_COGNITO_DOMAIN'].blank?
5 | CognitoUrls.init(ENV['AWS_COGNITO_DOMAIN'],
6 | ENV['AWS_COGNITO_REGION'])
7 |
8 | CognitoJwtKeysProvider.init(ENV['AWS_COGNITO_POOL_ID'])
9 | else
10 | puts "Skipping Cognito initialization"
11 | end
12 |
--------------------------------------------------------------------------------
/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 |
15 | # # Specify URI for violation reports
16 | # # policy.report_uri "/csp-violation-report-endpoint"
17 | # end
18 |
19 | # If you are using UJS then enable automatic nonce generation
20 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
21 |
22 | # Report CSP violations to a specified URI
23 | # For further information see the following documentation:
24 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
25 | # Rails.application.config.content_security_policy_report_only = true
26 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | Rails.application.config.session_store :active_record_store, :key => '_cognito-example_session'
2 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at http://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | threads threads_count, threads_count
9 |
10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
11 | #
12 | port ENV.fetch("PORT") { 3000 }
13 |
14 | # Specifies the `environment` that Puma will run in.
15 | #
16 | environment ENV.fetch("RAILS_ENV") { "development" }
17 |
18 | # Specifies the number of `workers` to boot in clustered mode.
19 | # Workers are forked webserver processes. If using threads and workers together
20 | # the concurrency of the application would be max `threads` * `workers`.
21 | # Workers do not work on JRuby or Windows (both of which do not support
22 | # processes).
23 | #
24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
25 |
26 | # Use the `preload_app!` method when specifying a `workers` number.
27 | # This directive tells Puma to first boot the application and load code
28 | # before forking the application. This takes advantage of Copy On Write
29 | # process behavior so workers use less memory.
30 | #
31 | # preload_app!
32 |
33 | # Allow puma to be restarted by `rails restart` command.
34 | plugin :tmp_restart
35 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | # For details on the DSL available within this file, see
3 | # http://guides.rubyonrails.org/routing.html
4 |
5 | get '/sign_in', as: 'signin', to: 'sessions#signin'
6 | get '/sign_out', as: 'signout', to: 'sessions#signout'
7 | get '/sign_up', as: 'signup', to: 'sessions#signup'
8 |
9 | get 'auth/sign_in', to: 'auth#signin'
10 | get 'auth/sign_out', to: 'auth#signout'
11 |
12 | root to: 'home#index'
13 | end
14 |
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | %w[
2 | .ruby-version
3 | .rbenv-vars
4 | tmp/restart.txt
5 | tmp/caching-dev.txt
6 | ].each { |path| Spring.watch(path) }
7 |
--------------------------------------------------------------------------------
/config/storage.yml:
--------------------------------------------------------------------------------
1 | test:
2 | service: Disk
3 | root: <%= Rails.root.join("tmp/storage") %>
4 |
5 | local:
6 | service: Disk
7 | root: <%= Rails.root.join("storage") %>
8 |
9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
10 | # amazon:
11 | # service: S3
12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
14 | # region: us-east-1
15 | # bucket: your_own_bucket
16 |
17 | # Remember not to checkin your GCS keyfile to a repository
18 | # google:
19 | # service: GCS
20 | # project: your_project
21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
22 | # bucket: your_own_bucket
23 |
24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
25 | # microsoft:
26 | # service: AzureStorage
27 | # storage_account_name: your_account_name
28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
29 | # container: your_container_name
30 |
31 | # mirror:
32 | # service: Mirror
33 | # primary: local
34 | # mirrors: [ amazon, google, microsoft ]
35 |
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/contrib/screenshots/screenshot_1.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/contrib/screenshots/screenshot_2.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/contrib/screenshots/screenshot_3.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/contrib/screenshots/screenshot_4.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/contrib/screenshots/screenshot_5.png
--------------------------------------------------------------------------------
/contrib/screenshots/screenshot_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/contrib/screenshots/screenshot_6.png
--------------------------------------------------------------------------------
/db/migrate/20190724144217_create_users.rb:
--------------------------------------------------------------------------------
1 | class CreateUsers < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :users do |t|
4 | t.string :subscriber, :null => false, :index => {unique: true}
5 | t.string :email, :null => false, :index => true
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20190724144310_create_cognito_sessions.rb:
--------------------------------------------------------------------------------
1 | class CreateCognitoSessions < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :cognito_sessions do |t|
4 | t.references :user, :index => true, :null => false
5 | t.integer :expire_time, :null => false
6 | t.integer :issued_time, :null => false
7 | t.string :audience, :null => false
8 | t.text :refresh_token, :null => false
9 |
10 | t.timestamps
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/db/migrate/20190724144814_add_sessions_table.rb:
--------------------------------------------------------------------------------
1 | class AddSessionsTable < ActiveRecord::Migration[5.2]
2 | def change
3 | create_table :sessions do |t|
4 | t.string :session_id, :null => false
5 | t.text :data
6 | t.timestamps
7 | end
8 |
9 | add_index :sessions, :session_id, :unique => true
10 | add_index :sessions, :updated_at
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # Note that this schema.rb definition is the authoritative source for your
6 | # database schema. If you need to create the application database on another
7 | # system, you should be using db:schema:load, not running all the migrations
8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 | # you'll amass, the slower it'll run and the greater likelihood for issues).
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2019_07_24_144814) do
14 |
15 | create_table "cognito_sessions", force: :cascade do |t|
16 | t.integer "user_id", null: false
17 | t.integer "expire_time", null: false
18 | t.integer "issued_time", null: false
19 | t.string "audience", null: false
20 | t.text "refresh_token", null: false
21 | t.datetime "created_at", null: false
22 | t.datetime "updated_at", null: false
23 | t.index ["user_id"], name: "index_cognito_sessions_on_user_id"
24 | end
25 |
26 | create_table "sessions", force: :cascade do |t|
27 | t.string "session_id", null: false
28 | t.text "data"
29 | t.datetime "created_at", null: false
30 | t.datetime "updated_at", null: false
31 | t.index ["session_id"], name: "index_sessions_on_session_id", unique: true
32 | t.index ["updated_at"], name: "index_sessions_on_updated_at"
33 | end
34 |
35 | create_table "users", force: :cascade do |t|
36 | t.string "subscriber", null: false
37 | t.string "email", null: false
38 | t.datetime "created_at", null: false
39 | t.datetime "updated_at", null: false
40 | t.index ["email"], name: "index_users_on_email"
41 | t.index ["subscriber"], name: "index_users_on_subscriber", unique: true
42 | end
43 |
44 | end
45 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
7 | # Character.create(name: 'Luke', movie: movies.first)
8 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/lib/assets/.keep
--------------------------------------------------------------------------------
/lib/cognito_client.rb:
--------------------------------------------------------------------------------
1 | require "cognito_jwt_keys"
2 | require "cognito_pool_tokens"
3 | require "cognito_urls"
4 |
5 | class CognitoClient
6 |
7 | def initialize(params = {})
8 | @pool_id = params[:pool_id] || ENV['AWS_COGNITO_POOL_ID']
9 | @client_id = params[:client_id] || ENV['AWS_COGNITO_APP_CLIENT_ID']
10 | @client_secret = params[:client_secret] || ENV['AWS_COGNITO_APP_CLIENT_SECRET']
11 | @redirect_uri = params[:redirect_uri]
12 | end
13 |
14 | def get_pool_tokens(authorization_code)
15 | params = {
16 | grant_type: 'authorization_code',
17 | code: authorization_code,
18 | client_id: @client_id,
19 | redirect_uri: @redirect_uri
20 | }
21 |
22 | resp = Excon.post(token_uri,
23 | :user => @client_id,
24 | :password => @client_secret,
25 | :body => URI.encode_www_form(params),
26 | :headers => { "Content-Type" => "application/x-www-form-urlencoded"})
27 |
28 | unless resp.status == 200
29 | Rails.logger.warn("Invalid code: #{authorization_code}: #{resp.body}")
30 | return nil
31 | end
32 |
33 | CognitoPoolTokens.new(CognitoJwtKeysProvider.keys, JSON.parse(resp.body))
34 | end
35 |
36 | # From: https://medium.com/tensult/how-to-refresh-aws-cognito-user-pool-tokens-d0e025cedd52
37 | def refresh_id_token(refresh_token)
38 | params = {
39 | ClientId: @client_id,
40 | AuthFlow: 'REFRESH_TOKEN_AUTH',
41 | AuthParameters: {
42 | REFRESH_TOKEN: refresh_token,
43 | SECRET_HASH: @client_secret
44 | }
45 | }
46 |
47 | hdrs = {
48 | "X-Amz-Target": "AWSCognitoIdentityProviderService.InitiateAuth",
49 | "Content-Type": "application/x-amz-json-1.1"
50 | }
51 |
52 | resp = Excon.post(CognitoUrls.refresh_token_uri,
53 | :headers => hdrs,
54 | :body => params.to_json)
55 | if resp.status != 200
56 | return nil
57 | end
58 |
59 | json = JSON.parse(resp.body)
60 |
61 | # Key names are different here, so need to translate :-/
62 | tokens = {
63 | 'id_token' => json['AuthenticationResult']['IdToken'],
64 | 'access_token' => json['AuthenticationResult']['AccessToken']
65 | }
66 |
67 | CognitoPoolTokens.new(CognitoJwtKeysProvider.keys, tokens)
68 | end
69 |
70 | private
71 |
72 | def token_uri
73 | CognitoUrls.token_uri
74 | end
75 | end
76 |
--------------------------------------------------------------------------------
/lib/cognito_jwt_keys.rb:
--------------------------------------------------------------------------------
1 | require "cognito_urls"
2 |
3 | class CognitoJwtKeysProvider
4 | class << self
5 | @jwt_keys = nil
6 |
7 | def init(pool_id)
8 | resp = Excon.get(key_url(pool_id))
9 | keys = JSON.parse(resp.body)
10 | keymap = Hash[keys["keys"].map {|key|
11 | [key["kid"], JSON::JWK.new(key)]
12 | }]
13 | @jwt_keys = CognitoJwtKeys.new(keymap)
14 | end
15 |
16 | def keys
17 | @jwt_keys
18 | end
19 |
20 | def key_url(pool_id)
21 | CognitoUrls.jwks_uri(pool_id)
22 | end
23 | end
24 | end
25 |
26 | class CognitoJwtKeys
27 | def initialize(keymap)
28 | @keys = keymap
29 | end
30 |
31 | def get(key_id, alg = 'RS256')
32 | key = @keys[key_id]
33 |
34 | unless key
35 | raise "No such JWK `#{key_id}`: #{@keys.keys}"
36 | end
37 |
38 | unless key[:alg] == alg
39 | raise "Algorithm not compatible #{key[:alg]} != #{alg}"
40 | end
41 |
42 | key
43 | end
44 | end
45 |
--------------------------------------------------------------------------------
/lib/cognito_pool_tokens.rb:
--------------------------------------------------------------------------------
1 | class CognitoPoolTokens
2 | def initialize(cognito_jwt_keys, token_hash)
3 | @cognito_jwt_keys = cognito_jwt_keys
4 | @token_hash = token_hash
5 | @token_cache = {}
6 | end
7 |
8 | def id_token; get_token('id_token'); end
9 | def access_token; get_token('access_token'); end
10 |
11 | # refresh token shouldn't be parsed
12 | def refresh_token; @token_hash['refresh_token']; end
13 |
14 | private
15 |
16 | def get_token(key)
17 | if @token_cache[key]
18 | return @token_cache[key]
19 | end
20 |
21 | @token_cache[key] = parse_token(@token_hash[key])
22 | end
23 |
24 | def parse_token(tok_str)
25 | hdr = jwt_header(tok_str)
26 | key = @cognito_jwt_keys.get(hdr['kid'], hdr['alg'])
27 | decoded = JSON::JWT.decode(tok_str, key)
28 |
29 | decoded
30 | end
31 |
32 | def jwt_header(jwt_string)
33 | parts = jwt_string.split(".")
34 | unless parts.length == 3
35 | raise "Not enough parts from JWT: #{jwt_string}"
36 | end
37 |
38 | JSON.parse(Base64.decode64(parts[0]))
39 | end
40 | end
41 |
--------------------------------------------------------------------------------
/lib/cognito_urls.rb:
--------------------------------------------------------------------------------
1 | class CognitoUrls
2 | AUTHORIZE_PATH = "/oauth2/authorize"
3 | TOKEN_PATH = "/oauth2/token"
4 | LOGIN_PATH = "/login"
5 | LOGOUT_PATH = "/logout"
6 | SIGNUP_PATH = "/signup"
7 |
8 | class << self
9 | @base_oauth_uri = nil
10 | @base_idp_uri = nil
11 |
12 | def init(domain, region)
13 | @base_oauth_uri = "https://%s.auth.%s.amazoncognito.com" % [domain, region]
14 | @base_idp_uri = "https://cognito-idp.%s.amazonaws.com" % [region]
15 | end
16 |
17 | def jwks_uri(pool_id)
18 | path = "/%s/.well-known/jwks.json" % [pool_id]
19 | URI.join(@base_idp_uri, path).to_s
20 | end
21 |
22 | def refresh_token_uri
23 | @base_idp_uri
24 | end
25 |
26 | def authorize_uri
27 | URI.join(@base_oauth_uri, AUTHORIZE_PATH).to_s
28 | end
29 |
30 | def token_uri
31 | URI.join(@base_oauth_uri, TOKEN_PATH).to_s
32 | end
33 |
34 | def login_uri(app_client_id, redirect_uri)
35 | path = "%s?response_type=code&client_id=%s&redirect_uri=%s" %
36 | [LOGIN_PATH, app_client_id, redirect_uri]
37 | URI.join(@base_oauth_uri, path).to_s
38 | end
39 |
40 | def logout_uri(app_client_id, redirect_uri)
41 | path = "%s?response_type=code&client_id=%s&logout_uri=%s" %
42 | [LOGOUT_PATH, app_client_id, redirect_uri]
43 | URI.join(@base_oauth_uri, path).to_s
44 | end
45 |
46 | def signup_uri(app_client_id, redirect_uri)
47 | path = "%s?response_type=code&client_id=%s&redirect_uri=%s" %
48 | [SIGNUP_PATH, app_client_id, redirect_uri]
49 | URI.join(@base_oauth_uri, path).to_s
50 | end
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/lib/tasks/.keep
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/log/.keep
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rails-cognito-user-pool-example",
3 | "private": true,
4 | "dependencies": {}
5 | }
6 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/public/favicon.ico
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/storage/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/storage/.keep
--------------------------------------------------------------------------------
/test/application_system_test_case.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5 | end
6 |
--------------------------------------------------------------------------------
/test/controllers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/test/controllers/.keep
--------------------------------------------------------------------------------
/test/controllers/auth_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class AuthControllerTest < ActionDispatch::IntegrationTest
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/controllers/home_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class HomeControllerTest < ActionDispatch::IntegrationTest
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/controllers/sessions_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class SessionsControllerTest < ActionDispatch::IntegrationTest
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/fixtures/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/test/fixtures/.keep
--------------------------------------------------------------------------------
/test/fixtures/cognito_sessions.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | # This model initially had no columns defined. If you add columns to the
4 | # model remove the '{}' from the fixture names and add the columns immediately
5 | # below each fixture, per the syntax in the comments below
6 | #
7 | one: {}
8 | # column: value
9 | #
10 | two: {}
11 | # column: value
12 |
--------------------------------------------------------------------------------
/test/fixtures/files/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/test/fixtures/files/.keep
--------------------------------------------------------------------------------
/test/fixtures/users.yml:
--------------------------------------------------------------------------------
1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2 |
3 | # This model initially had no columns defined. If you add columns to the
4 | # model remove the '{}' from the fixture names and add the columns immediately
5 | # below each fixture, per the syntax in the comments below
6 | #
7 | one: {}
8 | # column: value
9 | #
10 | two: {}
11 | # column: value
12 |
--------------------------------------------------------------------------------
/test/helpers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/test/helpers/.keep
--------------------------------------------------------------------------------
/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/test/integration/.keep
--------------------------------------------------------------------------------
/test/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/test/mailers/.keep
--------------------------------------------------------------------------------
/test/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/test/models/.keep
--------------------------------------------------------------------------------
/test/models/cognito_session_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class CognitoSessionTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/models/user_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class UserTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/system/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/test/system/.keep
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV['RAILS_ENV'] ||= 'test'
2 | require_relative '../config/environment'
3 | require 'rails/test_help'
4 |
5 | class ActiveSupport::TestCase
6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
7 | fixtures :all
8 |
9 | # Add more helper methods to be used by all tests here...
10 | end
11 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/tmp/.keep
--------------------------------------------------------------------------------
/vendor/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mheffner/rails-cognito-example/db8f23da13545f3e37cd640b30f81615dde7fe3d/vendor/.keep
--------------------------------------------------------------------------------