omniauth-himari 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2a1280e30d7d894b2db9ed7f9bbc12ddc2feb23c05b8b7e2114b0346ae314966
4
+ data.tar.gz: e9d350f407d5de7c26a72eaef8886935abb0334da60249e7137fd594af3a4eab
5
+ SHA512:
6
+ metadata.gz: 978c7555d9221b256309c56019a85e3030151627fc3f865d70c72976ebfe8008f4f2ae8d66e998e49eb8cbb0f955f794f38ebec7135dcc5d41cdda7b7c849f31
7
+ data.tar.gz: 82129a5f925165b1cf4edabfc0882d900acfbd81dad4da6bb6066b7edca7c94e9cdc4f163dfbd766542095e307d0812b1f515cb31416af21fbe899e1e39b3079
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2023-03-24
4
+
5
+ - Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2023 Sorah Fukumori
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # OmniAuth strategy for Himari
2
+
3
+ OmniAuth strategy to act as OIDC RP and use [Himari](https://github.com/sorah/himari) for OP.
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ # Gemfile
9
+ gem 'omniauth-himari'
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ### Setup
15
+
16
+ ```ruby
17
+ use OmniAuth::Builder do
18
+ provider :himari, {
19
+ site: 'https://himari.example.invalid',
20
+ client_id: '...',
21
+ client_secret: '...',
22
+
23
+ # verify_options: { ... } # JWT.decode verify options override
24
+ # verify_at_hash: true, # Verify at_hash returned in ID token
25
+
26
+ # use_userinfo: false # force use of userinfo endpoint for raw_info
27
+ # jwks_url: '...' # JWKs url to override (default=/public/jwks)
28
+
29
+ # user_agent: '...' # user-agent to send (default=OmniAuthHimari/X.Y.Z)
30
+
31
+ ## omniauth-oauth2 common strategy options
32
+ # client_options: { ... },
33
+ # pkce: true,
34
+ }
35
+ end
36
+ ```
37
+
38
+ ### Auth Hash
39
+
40
+ ```json
41
+ {
42
+ "provider": "himari",
43
+ "uid": "id_claim.sub",
44
+ "info": {
45
+ "name": "name || sub",
46
+ "nickname": "preferred_username",
47
+ "email": "email",
48
+ "first_name": "given_name",
49
+ "last_name": "family_name",
50
+ "image": "picture"
51
+ },
52
+ "credentials": {
53
+ "token": "access_token",
54
+ "expires_at": 42,
55
+ "expires": true,
56
+ "id_token": "id_token"
57
+ },
58
+ "extra": {
59
+ "userinfo_used": false,
60
+ "id_token": {
61
+ "claims": {
62
+ "sub": "sub",
63
+ "name": "name",
64
+ "preferred_username": "preferred_username",
65
+ "iss": "https://himari.example.invalid",
66
+ "aud": "...",
67
+ "iat": 1679595201,
68
+ "nbf": 1679595201,
69
+ "exp": 1679598801,
70
+ "at_hash": "..."
71
+ },
72
+ "header": {
73
+ "typ": "JWT",
74
+ "alg": "RS256",
75
+ "kid": "..."
76
+ }
77
+ },
78
+ "raw_info": {
79
+ "sub": "sub",
80
+ "name": "name",
81
+ "preferred_username": "preferred_username",
82
+ "iss": "https://himari.example.invalid",
83
+ "aud": "...",
84
+ "iat": 1679595201,
85
+ "nbf": 1679595201,
86
+ "exp": 1679598801,
87
+ "at_hash": "..."
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+
94
+ ## Development
95
+
96
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
97
+
98
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
99
+
100
+ ## Contributing
101
+
102
+ Bug reports and pull requests are welcome on GitHub at https://github.com/sorah/himari.
103
+
104
+ ## License
105
+
106
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ Bundler::GemHelper.tag_prefix = "omniauth-himari/"
9
+
10
+ task default: :spec
@@ -0,0 +1,177 @@
1
+ require 'omniauth'
2
+ require 'omniauth-oauth2'
3
+ require 'oauth2'
4
+ require 'digest/sha2'
5
+
6
+ require 'omniauth-himari/version'
7
+
8
+ module OmniAuth
9
+ module Strategies
10
+ class Himari < OmniAuth::Strategies::OAuth2
11
+ class IdTokenMissing < StandardError; end
12
+ class ConfigurationError < StandardError; end
13
+ class VerificationError < StandardError; end
14
+
15
+ option :name, 'himari'
16
+
17
+ option :client_options, {}
18
+ option :pkce, true
19
+
20
+ option :verify_options, {}
21
+ option :verify_at_hash, true
22
+
23
+ option :use_userinfo, false
24
+
25
+ option :jwks_url, nil
26
+
27
+ option :user_agent, nil
28
+
29
+ args %i(site)
30
+
31
+ def client
32
+ options.client_options.site ||= options.site
33
+ options.client_options.authorize_url ||= '/oidc/authorize'
34
+ options.client_options.token_url ||= '/public/oidc/token'
35
+ options.client_options.userinfo_url ||= '/public/oidc/userinfo'
36
+ options.client_options.access_token_class ||= AccessToken # https://gitlab.com/oauth-xx/oauth2/-/issues/628
37
+
38
+ options.client_options.connection_opts ||= {}
39
+ options.client_options.connection_opts[:headers] ||= {}
40
+ options.client_options.connection_opts[:headers] = {
41
+ 'User-Agent' => user_agent,
42
+ }.merge(options.client_options.connection_opts[:headers])
43
+
44
+ raise ConfigurationError, "client_id and client_secret is required" unless options.client_id && options.client_secret
45
+ raise ConfigurationError, "site is required" unless options.client_options.site
46
+ super
47
+ end
48
+
49
+ uid { raw_info['sub'] }
50
+
51
+ credentials do
52
+ retval = {
53
+ 'token' => access_token.token,
54
+ 'expires' => access_token.expires?,
55
+ 'expires_at' => access_token.expires_at,
56
+ 'id_token' => access_token.params && access_token.params['id_token'],
57
+ }
58
+ raise IdTokenMissing, 'id_token is missing' unless retval['id_token']
59
+ retval
60
+ end
61
+
62
+ info do
63
+ {
64
+ name: raw_info['name'] || raw_info['sub'],
65
+ nickname: raw_info['preferred_username'],
66
+ email: raw_info['email'],
67
+ first_name: raw_info['given_name'],
68
+ last_name: raw_info['family_name'],
69
+ image: raw_info['picture'],
70
+ }
71
+ end
72
+
73
+ extra do
74
+ {
75
+ userinfo_used: options.use_userinfo,
76
+ id_token: id_token.to_h,
77
+ raw_info: raw_info,
78
+ }
79
+ end
80
+
81
+ def verify_at_hash!(id_token)
82
+ return unless options.verify_at_hash
83
+
84
+ function = case id_token.header['alg'] # this is safe as we've verified
85
+ when 'ES256', 'RS256'; Digest::SHA256
86
+ when 'ES384'; Digest::SHA384
87
+ when 'ES512'; Digest::SHA512
88
+ else
89
+ raise VerificationError, "unknown hash function to verify at_hash for #{id_token.header['alg']}"
90
+ end
91
+
92
+ dgst = function.digest(access_token.token)
93
+ expected_at_hash = Base64.urlsafe_encode64(dgst[0, dgst.size/2], padding: false)
94
+
95
+ given_at_hash = id_token.claims['at_hash']
96
+
97
+ unless given_at_hash == expected_at_hash
98
+ raise VerificationError, "at_hash mismatch #{given_at_hash.inspect}, #{expected_at_hash.inspect}"
99
+ end
100
+ end
101
+
102
+ def raw_info
103
+ @raw_info ||= (!skip_info? && options.use_userinfo) ? access_token.get('/public/oidc/userinfo').parsed : id_token.claims
104
+ end
105
+
106
+ def faraday
107
+ @faraday ||= Faraday.new(options.site, headers: {'User-Agent' => user_agent}) do |b|
108
+ b.response :json
109
+ b.response :raise_error
110
+ end
111
+ end
112
+
113
+ def callback_url
114
+ options[:redirect_uri] || (full_host + callback_path) # https://github.com/omniauth/omniauth-oauth2/pull/142
115
+ end
116
+
117
+ def user_agent
118
+ options.user_agent || "OmniauthHimari/#{Omniauth::Himari::VERSION}"
119
+ end
120
+
121
+ def authorize_params
122
+ super.tap do |params|
123
+ params[:scope] = 'openid'
124
+ end
125
+ end
126
+
127
+ IdToken = Struct.new(:claims, :header)
128
+
129
+ def id_token
130
+ @id_token ||= begin
131
+ jwt = access_token.params['id_token'] or raise(IdTokenMissing, 'id_token is missing')
132
+ retval = IdToken.new(*JWT.decode(
133
+ jwt,
134
+ nil,
135
+ true,
136
+ {
137
+ algorithms: jwks.map { |k| k[:alg] }.compact.uniq,
138
+ jwks: jwks,
139
+ verify_aud: true,
140
+ aud: options.client_id,
141
+ verify_iss: true,
142
+ iss: options.site,
143
+ verify_expiration: true,
144
+ }.merge(options.verify_options)
145
+ ))
146
+ verify_at_hash!(retval)
147
+ retval
148
+ end
149
+ end
150
+
151
+ def jwks
152
+ JWT::JWK::Set.new(jwks_json).tap do |set|
153
+ set.filter! { |k| k[:use] == 'sig' }
154
+ end
155
+ end
156
+
157
+ def jwks_json
158
+ faraday.get(options.jwks_url || 'public/jwks').body
159
+ rescue Faraday::Error => e
160
+ raise JwksUnavailable, "failed to retrieve jwks; #{e.inspect}"
161
+ end
162
+
163
+ # https://github.com/omniauth/omniauth-oauth2/pull/142
164
+ class AccessToken < ::OAuth2::AccessToken
165
+ private def extra_tokens_warning(*)
166
+ # do nothing
167
+ end
168
+
169
+ def inspect
170
+ "#<#{self.class.name}:0x#{self.__id__.to_s(16)}>"
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Omniauth
4
+ module Himari
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ require 'omniauth-himari/version'
2
+ require 'omniauth/strategies/himari'
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/omniauth-himari/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "omniauth-himari"
7
+ spec.version = Omniauth::Himari::VERSION
8
+ spec.authors = ["Sorah Fukumori"]
9
+ spec.email = ["her@sorah.jp"]
10
+
11
+ spec.summary = "OmniAuth strategy for Himari"
12
+ spec.description = "OmniAuth strategy to act as OIDC RP and use [Himari](https://github.com/sorah/himari) for OP."
13
+ spec.homepage = "https://github.com/sorah/himari"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.7.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/sorah/himari"
19
+ #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ if ENV['HIMARI_LAMBDA_IMAGE']
24
+ spec.files = Dir.chdir(__dir__) { Dir["./**/*"] }.reject { |f| (File.expand_path(f) == __FILE__) }
25
+ else
26
+ spec.files = Dir.chdir(__dir__) do
27
+ `git ls-files -z`.split("\x0").reject do |f|
28
+ (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
29
+ end
30
+ end
31
+ end
32
+ spec.bindir = "exe"
33
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
34
+ spec.require_paths = ["lib"]
35
+
36
+ spec.add_dependency 'omniauth'
37
+ spec.add_dependency 'omniauth-oauth2'
38
+ spec.add_dependency 'oauth2'
39
+ spec.add_dependency 'faraday'
40
+
41
+ # Uncomment to register a new dependency of your gem
42
+ # spec.add_dependency "example-gem", "~> 1.0"
43
+
44
+ # For more information and examples about making a new gem, check out our
45
+ # guide at: https://bundler.io/guides/creating_gem.html
46
+ end
@@ -0,0 +1,6 @@
1
+ module Omniauth
2
+ module Himari
3
+ VERSION: String
4
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-himari
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sorah Fukumori
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2023-03-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: omniauth
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: omniauth-oauth2
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: oauth2
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: faraday
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: OmniAuth strategy to act as OIDC RP and use [Himari](https://github.com/sorah/himari)
70
+ for OP.
71
+ email:
72
+ - her@sorah.jp
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".rspec"
78
+ - CHANGELOG.md
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - lib/omniauth-himari.rb
83
+ - lib/omniauth-himari/version.rb
84
+ - lib/omniauth/strategies/himari.rb
85
+ - omniauth-himari.gemspec
86
+ - sig/omniauth/himari.rbs
87
+ homepage: https://github.com/sorah/himari
88
+ licenses:
89
+ - MIT
90
+ metadata:
91
+ homepage_uri: https://github.com/sorah/himari
92
+ source_code_uri: https://github.com/sorah/himari
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: 2.7.0
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubygems_version: 3.1.6
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: OmniAuth strategy for Himari
112
+ test_files: []