openid-token-proxy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +6 -0
  5. data/CHANGELOG.md +5 -0
  6. data/Gemfile +4 -0
  7. data/Guardfile +41 -0
  8. data/LICENSE.md +22 -0
  9. data/README.md +211 -0
  10. data/Rakefile +16 -0
  11. data/app/controllers/openid_token_proxy/application_controller.rb +4 -0
  12. data/app/controllers/openid_token_proxy/callback_controller.rb +22 -0
  13. data/config/initializers/inflections.rb +3 -0
  14. data/config/routes.rb +3 -0
  15. data/docs/diagrams.sketch +0 -0
  16. data/docs/openid-token-proxy-flow.png +0 -0
  17. data/docs/regular-openid-flow.png +0 -0
  18. data/lib/openid-token-proxy.rb +1 -0
  19. data/lib/openid_token_proxy/client.rb +48 -0
  20. data/lib/openid_token_proxy/config.rb +56 -0
  21. data/lib/openid_token_proxy/engine.rb +5 -0
  22. data/lib/openid_token_proxy/error.rb +7 -0
  23. data/lib/openid_token_proxy/token/authentication.rb +54 -0
  24. data/lib/openid_token_proxy/token/expired.rb +12 -0
  25. data/lib/openid_token_proxy/token/invalid_application.rb +12 -0
  26. data/lib/openid_token_proxy/token/invalid_audience.rb +12 -0
  27. data/lib/openid_token_proxy/token/invalid_issuer.rb +12 -0
  28. data/lib/openid_token_proxy/token/malformed.rb +12 -0
  29. data/lib/openid_token_proxy/token/refresh.rb +30 -0
  30. data/lib/openid_token_proxy/token/required.rb +12 -0
  31. data/lib/openid_token_proxy/token/unverifiable_signature.rb +12 -0
  32. data/lib/openid_token_proxy/token.rb +80 -0
  33. data/lib/openid_token_proxy/version.rb +3 -0
  34. data/lib/openid_token_proxy.rb +40 -0
  35. data/openid-token-proxy.gemspec +35 -0
  36. data/spec/controllers/openid_token_proxy/callback_controller_spec.rb +72 -0
  37. data/spec/dummy/README.rdoc +28 -0
  38. data/spec/dummy/Rakefile +6 -0
  39. data/spec/dummy/app/assets/images/.keep +0 -0
  40. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  41. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  42. data/spec/dummy/app/controllers/accounts_controller.rb +10 -0
  43. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  44. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  45. data/spec/dummy/app/controllers/home_controller.rb +7 -0
  46. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  47. data/spec/dummy/app/mailers/.keep +0 -0
  48. data/spec/dummy/app/models/.keep +0 -0
  49. data/spec/dummy/app/models/concerns/.keep +0 -0
  50. data/spec/dummy/app/views/home/index.html.erb +25 -0
  51. data/spec/dummy/app/views/layouts/application.html.erb +54 -0
  52. data/spec/dummy/bin/bundle +3 -0
  53. data/spec/dummy/bin/rails +4 -0
  54. data/spec/dummy/bin/rake +4 -0
  55. data/spec/dummy/config/application.rb +27 -0
  56. data/spec/dummy/config/boot.rb +5 -0
  57. data/spec/dummy/config/environment.rb +5 -0
  58. data/spec/dummy/config/environments/development.rb +34 -0
  59. data/spec/dummy/config/environments/production.rb +75 -0
  60. data/spec/dummy/config/environments/test.rb +39 -0
  61. data/spec/dummy/config/initializers/assets.rb +8 -0
  62. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  63. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  64. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  65. data/spec/dummy/config/initializers/inflections.rb +16 -0
  66. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  67. data/spec/dummy/config/initializers/openid.rb +5 -0
  68. data/spec/dummy/config/initializers/session_store.rb +3 -0
  69. data/spec/dummy/config/initializers/wrap_parameters.rb +9 -0
  70. data/spec/dummy/config/locales/en.yml +23 -0
  71. data/spec/dummy/config/routes.rb +5 -0
  72. data/spec/dummy/config/secrets.yml +22 -0
  73. data/spec/dummy/config.ru +4 -0
  74. data/spec/dummy/lib/assets/.keep +0 -0
  75. data/spec/dummy/log/.keep +0 -0
  76. data/spec/dummy/public/404.html +67 -0
  77. data/spec/dummy/public/422.html +67 -0
  78. data/spec/dummy/public/500.html +66 -0
  79. data/spec/dummy/public/favicon.ico +0 -0
  80. data/spec/fixtures/keys.json +26 -0
  81. data/spec/fixtures/openid-configuration.json +30 -0
  82. data/spec/lib/openid_token_proxy/client_spec.rb +150 -0
  83. data/spec/lib/openid_token_proxy/config_spec.rb +201 -0
  84. data/spec/lib/openid_token_proxy/error_spec.rb +11 -0
  85. data/spec/lib/openid_token_proxy/token/authentication_spec.rb +67 -0
  86. data/spec/lib/openid_token_proxy/token/refresh_spec.rb +71 -0
  87. data/spec/lib/openid_token_proxy/token_spec.rb +138 -0
  88. data/spec/lib/openid_token_proxy_spec.rb +38 -0
  89. data/spec/spec_helper.rb +88 -0
  90. data/spec/support/env.rb +4 -0
  91. data/spec/support/fixture.rb +3 -0
  92. metadata +359 -0
@@ -0,0 +1,3 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ Rails.application.config.action_dispatch.cookies_serializer = :json
@@ -0,0 +1,4 @@
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]
@@ -0,0 +1,16 @@
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
@@ -0,0 +1,4 @@
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
@@ -0,0 +1,5 @@
1
+ OpenIDTokenProxy.configure do |config|
2
+ config.token_acquirement_hook = proc { |token|
3
+ main_app.root_url + "?token=#{token}&refresh_token=#{token.refresh_token}"
4
+ }
5
+ end
@@ -0,0 +1,3 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ Rails.application.config.session_store :cookie_store, key: '_dummy_session'
@@ -0,0 +1,9 @@
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] if respond_to?(:wrap_parameters)
9
+ end
@@ -0,0 +1,23 @@
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
+ # To learn more, please read the Rails Internationalization guide
20
+ # available at http://guides.rubyonrails.org/i18n.html.
21
+
22
+ en:
23
+ hello: "Hello world"
@@ -0,0 +1,5 @@
1
+ Rails.application.routes.draw do
2
+ root to: 'home#index'
3
+ resource :account
4
+ mount OpenIDTokenProxy::Engine, at: '/auth'
5
+ end
@@ -0,0 +1,22 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Your secret key is used for verifying the integrity of signed cookies.
4
+ # If you change this key, all old signed cookies will become invalid!
5
+
6
+ # Make sure the secret is at least 30 characters and all random,
7
+ # no regular words or you'll be exposed to dictionary attacks.
8
+ # You can use `rake secret` to generate a secure secret key.
9
+
10
+ # Make sure the secrets in this file are kept private
11
+ # if you're sharing your code publicly.
12
+
13
+ development:
14
+ secret_key_base: 108dd645d65f238e23c78f443335185beebc37a18ddb6226cbb54331b31fc3c03a92dd33cdffcd0dcdd0a361f2ec6a2d8c5a0553808de41f8be5ae2119de627c
15
+
16
+ test:
17
+ secret_key_base: ed6785ee3b0d5e4c6a705454ba80cd699be6f1cea7b1a24494ffab9e044dc2bb5834b2f6239991d46e6df2a6550cf36b3f6f9c40589eebf21e2c11f8256eb166
18
+
19
+ # Do not keep production secrets in the repository,
20
+ # instead read values from the environment.
21
+ production:
22
+ secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
@@ -0,0 +1,4 @@
1
+ # This file is used by Rack-based servers to start the application.
2
+
3
+ require ::File.expand_path('../config/environment', __FILE__)
4
+ run Rails.application
File without changes
File without changes
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/404.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,26 @@
1
+ {
2
+ "keys": [
3
+ {
4
+ "kty": "RSA",
5
+ "use": "sig",
6
+ "kid": "kriMPdmBvx68skT8-mPAB3BseeA",
7
+ "x5t": "kriMPdmBvx68skT8-mPAB3BseeA",
8
+ "n": "kSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS_AHsBeQPqYygfYVJL6_EgzVuwRk5txr9e3n1uml94fLyq_AXbwo9yAduf4dCHTP8CWR1dnDR-Qnz_4PYlWVEuuHHONOw_blbfdMjhY-C_BYM2E3pRxbohBb3x__CfueV7ddz2LYiH3wjz0QS_7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd_GTgWN8A-6SN1r4hzpjFKFLbZnBt77ACSiYx-IHK4Mp-NaVEi5wQtSsjQtI--XsokxRDqYLwus1I1SihgbV_STTg5enufuw",
9
+ "e": "AQAB",
10
+ "x5c": [
11
+ "MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ"
12
+ ]
13
+ },
14
+ {
15
+ "kty": "RSA",
16
+ "use": "sig",
17
+ "kid": "MnC_VZcATfM5pOYiJHMba9goEKY",
18
+ "x5t": "MnC_VZcATfM5pOYiJHMba9goEKY",
19
+ "n": "vIqz-4-ER_vNWLON9yv8hIYV737JQ6rCl6XfzOC628seYUPf0TaGk91CFxefhzh23V9Tkq-RtwN1Vs_z57hO82kkzL-cQHZX3bMJD-GEGOKXCEXURN7VMyZWMAuzQoW9vFb1k3cR1RW_EW_P-C8bb2dCGXhBYqPfHyimvz2WarXhntPSbM5XyS5v5yCw5T_Vuwqqsio3V8wooWGMpp61y12NhN8bNVDQAkDPNu2DT9DXB1g0CeFINp_KAS_qQ2Kq6TSvRHJqxRR68RezYtje9KAqwqx4jxlmVAQy0T3-T-IAbsk1wRtWDndhO6s1Os-dck5TzyZ_dNOhfXgelixLUQ",
20
+ "e": "AQAB",
21
+ "x5c": [
22
+ "MIIC4jCCAcqgAwIBAgIQQNXrmzhLN4VGlUXDYCRT3zANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE0MTAyODAwMDAwMFoXDTE2MTAyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALyKs/uPhEf7zVizjfcr/ISGFe9+yUOqwpel38zgutvLHmFD39E2hpPdQhcXn4c4dt1fU5KvkbcDdVbP8+e4TvNpJMy/nEB2V92zCQ/hhBjilwhF1ETe1TMmVjALs0KFvbxW9ZN3EdUVvxFvz/gvG29nQhl4QWKj3x8opr89lmq14Z7T0mzOV8kub+cgsOU/1bsKqrIqN1fMKKFhjKaetctdjYTfGzVQ0AJAzzbtg0/Q1wdYNAnhSDafygEv6kNiquk0r0RyasUUevEXs2LY3vSgKsKseI8ZZlQEMtE9/k/iAG7JNcEbVg53YTurNTrPnXJOU88mf3TToX14HpYsS1ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfolx45w0i8CdAUjjeAaYdhG9+NDHxop0UvNOqlGqYJexqPLuvX8iyUaYxNGzZxFgGI3GpKfmQP2JQWQ1E5JtY/n8iNLOKRMwqkuxSCKJxZJq4Sl/m/Yv7TS1P5LNgAj8QLCypxsWrTAmq2HSpkeSk4JBtsYxX6uhbGM/K1sEktKybVTHu22/7TmRqWTmOUy9wQvMjJb2IXdMGLG3hVntN/WWcs5w8vbt1i8Kk6o19W2MjZ95JaECKjBDYRlhG1KmSBtrsKsCBQoBzwH/rXfksTO9JoUYLXiW0IppB7DhNH4PJ5hZI91R8rR0H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ=="
23
+ ]
24
+ }
25
+ ]
26
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "issuer": "https://sts.windows.net/{tenantid}/",
3
+ "authorization_endpoint": "https://login.windows.net/common/oauth2/authorize",
4
+ "token_endpoint": "https://login.windows.net/common/oauth2/token",
5
+ "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt"],
6
+ "jwks_uri": "https://login.windows.net/common/discovery/keys",
7
+ "response_types_supported": ["code", "id_token", "code id_token", "token"],
8
+ "response_modes_supported": ["query", "fragment", "form_post"],
9
+ "subject_types_supported": ["pairwise"], "scopes_supported": ["openid"],
10
+ "id_token_signing_alg_values_supported": ["RS256"],
11
+ "claims_supported": [
12
+ "sub",
13
+ "iss",
14
+ "aud",
15
+ "exp",
16
+ "iat",
17
+ "auth_time",
18
+ "acr",
19
+ "amr",
20
+ "nonce",
21
+ "email",
22
+ "given_name",
23
+ "family_name",
24
+ "nickname"
25
+ ],
26
+ "microsoft_multi_refresh_token": true,
27
+ "check_session_iframe": "https://login.windows.net/common/oauth2/checksession",
28
+ "end_session_endpoint": "https://login.windows.net/common/oauth2/logout",
29
+ "userinfo_endpoint": "https://login.windows.net/common/openid/userinfo"
30
+ }
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ require 'securerandom'
4
+
5
+ RSpec.describe OpenIDTokenProxy::Client do
6
+ subject { described_class.new config }
7
+ let(:config) do
8
+ OpenIDTokenProxy::Config.new do |config|
9
+ config.client_id = 'id'
10
+ config.issuer = 'https://example.com'
11
+ config.resource = nil
12
+ config.domain_hint = nil
13
+ config.prompt = nil
14
+ config.authorization_endpoint = 'https://example.com/auth'
15
+ config.token_endpoint = 'https://example.com/token'
16
+ config.userinfo_endpoint = 'https://example.com/users'
17
+ end
18
+ end
19
+
20
+ describe '#config' do
21
+ it 'defaults to OpenIDTokenProxy.config' do
22
+ client = described_class.new
23
+ expect(client.config).to eq OpenIDTokenProxy.config
24
+ end
25
+
26
+ it 'may be given explicitly' do
27
+ expect(subject.config).to eq config
28
+ end
29
+ end
30
+
31
+ describe '#authorization_uri' do
32
+ it 'may be explicitly set through configuration' do
33
+ config.authorization_uri = 'overridden'
34
+ expect(subject.authorization_uri).to eq 'overridden'
35
+ end
36
+
37
+ context 'when not explicitly set' do
38
+ let(:expected_auth_uri) {
39
+ 'https://example.com/auth?client_id=id&response_type=code&scope=openid'
40
+ }
41
+
42
+ it 'builds OpenID authorization URI' do
43
+ expect(subject.authorization_uri).to eq expected_auth_uri
44
+ end
45
+
46
+ context 'when domain hint given' do
47
+ it 'includes domain hint' do
48
+ hint = SecureRandom.hex
49
+ config.domain_hint = hint
50
+ expect(subject.authorization_uri).to include "domain_hint=#{hint}"
51
+ end
52
+ end
53
+
54
+ context 'when prompt given' do
55
+ it 'includes prompt' do
56
+ prompt = SecureRandom.hex
57
+ config.prompt = prompt
58
+ expect(subject.authorization_uri).to include "prompt=#{prompt}"
59
+ end
60
+ end
61
+
62
+ context 'when resource given' do
63
+ it 'includes resource' do
64
+ resource = SecureRandom.hex
65
+ config.resource = resource
66
+ expect(subject.authorization_uri).to include "resource=#{resource}"
67
+ end
68
+ end
69
+
70
+ context 'when redirect_uri given' do
71
+ it 'includes redirect_uri' do
72
+ uri = SecureRandom.hex
73
+ config.redirect_uri = uri
74
+ expect(subject.authorization_uri).to include "redirect_uri=#{uri}"
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ describe '#retrieve_token!' do
81
+ let(:client) {
82
+ double(
83
+ 'authorization_code=' => nil,
84
+ 'refresh_token=' => nil
85
+ )
86
+ }
87
+ let(:access_token) { 'access token' }
88
+ let(:id_token) { 'id token' }
89
+ let(:refresh_token) { 'refresh token' }
90
+ let(:response) {
91
+ double(
92
+ access_token: access_token,
93
+ id_token: id_token,
94
+ refresh_token: refresh_token
95
+ )
96
+ }
97
+ let(:token) {
98
+ OpenIDTokenProxy::Token.new(access_token, id_token)
99
+ }
100
+
101
+ before do
102
+ expect(subject).to receive(:new_client).and_return client
103
+ allow(OpenIDTokenProxy::Token).to receive(:decode!).and_return token
104
+ end
105
+
106
+ context 'using auth code' do
107
+ context 'when auth code could not be exchanged' do
108
+ it 'raises' do
109
+ error = Rack::OAuth2::Client::Error.new 400, {}
110
+ expect(client).to receive(:access_token!).and_raise error
111
+ expect do
112
+ subject.retrieve_token! auth_code: 'malformed auth code'
113
+ end.to raise_error OpenIDTokenProxy::Client::AuthCodeError
114
+ end
115
+ end
116
+
117
+ context 'when auth code is valid' do
118
+ it 'returns token instance' do
119
+ expect(client).to receive(:access_token!).and_return response
120
+ token = subject.retrieve_token! auth_code: 'valid auth code'
121
+ expect(token.access_token).to eq access_token
122
+ expect(token.id_token).to eq id_token
123
+ expect(token.refresh_token).to eq refresh_token
124
+ end
125
+ end
126
+ end
127
+
128
+ context 'using refresh token' do
129
+ context 'when refresh token could not be exchanged' do
130
+ it 'raises' do
131
+ error = Rack::OAuth2::Client::Error.new 400, {}
132
+ expect(client).to receive(:access_token!).and_raise error
133
+ expect do
134
+ subject.retrieve_token! refresh_token: 'malformed refresh token'
135
+ end.to raise_error OpenIDTokenProxy::Client::RefreshTokenError
136
+ end
137
+ end
138
+
139
+ context 'when refresh token is valid' do
140
+ it 'returns token instance' do
141
+ expect(client).to receive(:access_token!).and_return response
142
+ token = subject.retrieve_token! refresh_token: 'valid refresh token'
143
+ expect(token.access_token).to eq access_token
144
+ expect(token.id_token).to eq id_token
145
+ expect(token.refresh_token).to eq refresh_token
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end