omniauth-openai 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +6 -0
- data/LICENSE +18 -0
- data/README.md +110 -0
- data/lib/omniauth/openai/key_set.rb +48 -0
- data/lib/omniauth/openai/version.rb +7 -0
- data/lib/omniauth/openai.rb +4 -0
- data/lib/omniauth/strategies/openai.rb +178 -0
- data/lib/omniauth-openai.rb +3 -0
- data/omniauth-openai.gemspec +38 -0
- metadata +148 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c4510db682d5cc881cd21ff66a13b91f19c59e70f2c34e23c38e24b9b7d954d4
|
|
4
|
+
data.tar.gz: 8e4c7ae32887385fc221e700af78787e87b017369eb5de1acb1f0e5efa5c2806
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 73c540a83c85e9fa03f71d1be70d1ea2c811273cc1d9c8e744b66b60a3d81e9b3de17649509a998042e5de63dae8656c3828d2a68143695d635e4a6dc0ca9cbc
|
|
7
|
+
data.tar.gz: 73a11709ac1f2366346b311a2ce2c8c6e26a9cbfd6eb5d2e837bc7cbb871522c0d59d3b3ac7346ab84319e3e1a3a23a4a0e33534d85a605b38bf0e9a4baf6540
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ajaynomics
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
6
|
+
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
8
|
+
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
12
|
+
portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
15
|
+
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
16
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
18
|
+
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# omniauth-openai
|
|
2
|
+
|
|
3
|
+
An [OmniAuth](https://github.com/omniauth/omniauth) strategy for
|
|
4
|
+
[Sign in with ChatGPT](https://developers.openai.com/siwc/website), OpenAI's
|
|
5
|
+
OpenID Connect sign-in.
|
|
6
|
+
|
|
7
|
+
It runs the authorization code flow with PKCE and a nonce, then verifies the
|
|
8
|
+
ID token's signature, issuer, audience, expiry, and nonce against OpenAI's
|
|
9
|
+
published keys before your application sees an identity. It makes no userinfo
|
|
10
|
+
call and hands your application no OpenAI tokens: sign-in proves who someone
|
|
11
|
+
is, nothing more.
|
|
12
|
+
|
|
13
|
+
Unofficial. Not affiliated with or endorsed by OpenAI.
|
|
14
|
+
|
|
15
|
+
## Before you start
|
|
16
|
+
|
|
17
|
+
Sign in with ChatGPT is in limited access. Request it through
|
|
18
|
+
[OpenAI's interest form](https://openai.com/form/sign-in-with-chatgpt-interest/).
|
|
19
|
+
OpenAI then issues a client ID (`oaiapp_…`), and a client secret if you ask for
|
|
20
|
+
a confidential client, and registers your exact callback URL, for example
|
|
21
|
+
`https://example.com/users/auth/openai/callback`.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
gem "omniauth-openai"
|
|
27
|
+
gem "omniauth-rails_csrf_protection" # Rails: sign-in must start with a POST
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Use
|
|
31
|
+
|
|
32
|
+
### Devise
|
|
33
|
+
|
|
34
|
+
```ruby
|
|
35
|
+
# config/initializers/devise.rb
|
|
36
|
+
config.omniauth :openai, ENV["OPENAI_CLIENT_ID"], ENV["OPENAI_CLIENT_SECRET"]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
# app/models/user.rb
|
|
41
|
+
devise :omniauthable, omniauth_providers: %i[openai]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
```erb
|
|
45
|
+
<%= button_to "Continue with ChatGPT", user_openai_omniauth_authorize_path, data: { turbo: false } %>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Plain Rack or Rails without Devise
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
use OmniAuth::Builder do
|
|
52
|
+
provider :openai, ENV["OPENAI_CLIENT_ID"], ENV["OPENAI_CLIENT_SECRET"]
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
A public client has no secret: pass `nil` and the strategy sends only its
|
|
57
|
+
client ID.
|
|
58
|
+
|
|
59
|
+
### The auth hash
|
|
60
|
+
|
|
61
|
+
```ruby
|
|
62
|
+
{
|
|
63
|
+
"provider" => "openai",
|
|
64
|
+
"uid" => "user-…", # the ID token's sub: key accounts on this
|
|
65
|
+
"info" => {
|
|
66
|
+
"email" => "person@example.com", # present only when email_verified is true
|
|
67
|
+
"name" => "Chat Person",
|
|
68
|
+
"image" => "https://…"
|
|
69
|
+
},
|
|
70
|
+
"credentials" => {},
|
|
71
|
+
"extra" => { "raw_info" => { … all verified ID token claims … } }
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Key accounts on `provider` and `uid`, not email: one email can belong to more
|
|
76
|
+
than one OpenAI account. If you link to an existing account by email, link only
|
|
77
|
+
to one whose email your application has itself confirmed, or an attacker who
|
|
78
|
+
registers the address first gains the real owner's sign-in.
|
|
79
|
+
|
|
80
|
+
## Options
|
|
81
|
+
|
|
82
|
+
| Option | Default | Meaning |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `scope` | `"openid profile email"` | Requested scopes. |
|
|
85
|
+
| `client_auth_method` | `:basic` with a secret, `:none` without | `:basic` (client_secret_basic), `:post` (client_secret_post), or `:none`. |
|
|
86
|
+
| `redirect_uri` | the callback URL | Set it when a proxy changes the host the app sees. Must match the registered URL exactly. |
|
|
87
|
+
| `leeway` | `5` | Seconds of clock skew allowed on `exp` and `iat`. |
|
|
88
|
+
| `issuer`, `jwks_uri`, `client_options` | OpenAI's | Endpoints from `https://auth.openai.com/.well-known/openid-configuration`. |
|
|
89
|
+
|
|
90
|
+
Signing keys are cached per process for an hour. A token signed with a key the
|
|
91
|
+
cache lacks triggers one fresh download, at most once every five minutes.
|
|
92
|
+
|
|
93
|
+
## Failures
|
|
94
|
+
|
|
95
|
+
Every verification failure ends in OmniAuth's failure path with
|
|
96
|
+
`:invalid_credentials`, so a bad token never reaches your callback. A forged or
|
|
97
|
+
missing `state` fails as `:csrf_detected`; a person who declines consent fails
|
|
98
|
+
as `:access_denied`.
|
|
99
|
+
|
|
100
|
+
## Development
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
bundle install
|
|
104
|
+
bundle exec rake test
|
|
105
|
+
JWT_VERSION="~> 2.10" bundle update jwt && bundle exec rake test
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module OmniAuth
|
|
4
|
+
module OpenAI
|
|
5
|
+
# The issuer's signing keys, shared by every request in the process.
|
|
6
|
+
#
|
|
7
|
+
# Keys are kept for +ttl+ seconds. When a token names a key the set lacks,
|
|
8
|
+
# ruby-jwt asks again with +kid_not_found+, and the set downloads afresh so
|
|
9
|
+
# a rotated key is picked up at once. Those forced downloads happen at most
|
|
10
|
+
# once per +refresh_interval+, so a stream of bad tokens cannot turn into a
|
|
11
|
+
# stream of requests to the issuer.
|
|
12
|
+
class KeySet
|
|
13
|
+
def initialize(ttl: 3600, refresh_interval: 300, clock: -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) })
|
|
14
|
+
@ttl = ttl
|
|
15
|
+
@refresh_interval = refresh_interval
|
|
16
|
+
@clock = clock
|
|
17
|
+
@lock = Mutex.new
|
|
18
|
+
@keys = nil
|
|
19
|
+
@fetched_at = nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Returns the JWKS hash, calling the block to download it when needed.
|
|
23
|
+
def fetch(kid_not_found: false)
|
|
24
|
+
@lock.synchronize do
|
|
25
|
+
if stale? || (kid_not_found && refreshable?)
|
|
26
|
+
@keys = yield
|
|
27
|
+
@fetched_at = now
|
|
28
|
+
end
|
|
29
|
+
@keys
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def stale?
|
|
36
|
+
@keys.nil? || now - @fetched_at >= @ttl
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def refreshable?
|
|
40
|
+
now - @fetched_at >= @refresh_interval
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def now
|
|
44
|
+
@clock.call
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "jwt"
|
|
5
|
+
require "omniauth-oauth2"
|
|
6
|
+
require "omniauth/openai/key_set"
|
|
7
|
+
require "rack/utils"
|
|
8
|
+
require "securerandom"
|
|
9
|
+
require "uri"
|
|
10
|
+
|
|
11
|
+
module OmniAuth
|
|
12
|
+
module Strategies
|
|
13
|
+
# Sign in with ChatGPT: OpenID Connect over the authorization code flow,
|
|
14
|
+
# with PKCE and a nonce. See https://developers.openai.com/siwc/website.
|
|
15
|
+
#
|
|
16
|
+
# The strategy establishes who the person is and nothing more. It trusts
|
|
17
|
+
# only the ID token, after checking its signature, issuer, audience,
|
|
18
|
+
# expiry, and nonce. It makes no userinfo call and returns no OpenAI
|
|
19
|
+
# tokens, because identity sign-in needs none.
|
|
20
|
+
#
|
|
21
|
+
# The endpoints come from https://auth.openai.com/.well-known/openid-configuration.
|
|
22
|
+
# They are fixed options rather than discovered at boot, so a slow
|
|
23
|
+
# discovery request cannot hold up an application starting.
|
|
24
|
+
class OpenAI < OmniAuth::Strategies::OAuth2
|
|
25
|
+
ISSUER = "https://auth.openai.com"
|
|
26
|
+
KEY_SETS = Hash.new { |sets, uri| sets[uri] = OmniAuth::OpenAI::KeySet.new }
|
|
27
|
+
KEY_SETS_LOCK = Mutex.new
|
|
28
|
+
|
|
29
|
+
option :name, "openai"
|
|
30
|
+
option :scope, "openid profile email"
|
|
31
|
+
option :pkce, true
|
|
32
|
+
option :issuer, ISSUER
|
|
33
|
+
option :jwks_uri, "#{ISSUER}/.well-known/jwks.json"
|
|
34
|
+
# :basic (client_secret_basic), :post (client_secret_post), or :none for
|
|
35
|
+
# a public client, the three methods OpenAI's discovery document lists.
|
|
36
|
+
# Left nil, a client with a secret uses :basic, the OpenID Connect
|
|
37
|
+
# default, and one without uses :none.
|
|
38
|
+
option :client_auth_method, nil
|
|
39
|
+
option :leeway, 5
|
|
40
|
+
option :client_options, {
|
|
41
|
+
site: ISSUER,
|
|
42
|
+
authorize_url: "/api/accounts/authorize",
|
|
43
|
+
token_url: "/api/accounts/oauth/token"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
uid { id_token_claims.fetch("sub") }
|
|
47
|
+
|
|
48
|
+
# Applications commonly match accounts by email, so the email is
|
|
49
|
+
# included only when OpenAI says the person verified it.
|
|
50
|
+
info do
|
|
51
|
+
{
|
|
52
|
+
email: (id_token_claims["email"] if id_token_claims["email_verified"] == true),
|
|
53
|
+
name: id_token_claims["name"],
|
|
54
|
+
first_name: id_token_claims["given_name"],
|
|
55
|
+
last_name: id_token_claims["family_name"],
|
|
56
|
+
nickname: id_token_claims["preferred_username"],
|
|
57
|
+
image: id_token_claims["picture"]
|
|
58
|
+
}.compact
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
extra do
|
|
62
|
+
{ "raw_info" => id_token_claims }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# OmniAuth merges credentials blocks down the class chain, so the
|
|
66
|
+
# parent's token keys can only be dropped by overriding the method.
|
|
67
|
+
def credentials
|
|
68
|
+
{}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The token request carries client_id in the body for every method.
|
|
72
|
+
# oauth2's :tls_client_auth scheme does exactly that and nothing more;
|
|
73
|
+
# the secret, if any, is added by #token_params.
|
|
74
|
+
def client
|
|
75
|
+
::OAuth2::Client.new(options.client_id, options.client_secret,
|
|
76
|
+
deep_symbolize(options.client_options).merge(auth_scheme: :tls_client_auth))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# OAuth2 appends the callback's query string, but OpenAI compares
|
|
80
|
+
# redirect_uri exactly with the one sent to the authorize endpoint.
|
|
81
|
+
def callback_url
|
|
82
|
+
options.redirect_uri || (full_host + callback_path)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def authorize_params
|
|
86
|
+
super.tap do |params|
|
|
87
|
+
params[:nonce] = session[nonce_session_key] = SecureRandom.urlsafe_base64(32)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def token_params
|
|
92
|
+
params = super
|
|
93
|
+
case client_auth_method
|
|
94
|
+
when :basic then params.merge(headers: { "Authorization" => basic_authorization })
|
|
95
|
+
when :post then params.merge(client_secret: options.client_secret)
|
|
96
|
+
else params
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
attr_reader :id_token_claims
|
|
103
|
+
|
|
104
|
+
# Runs inside OAuth2#callback_phase, which turns a CallbackError into an
|
|
105
|
+
# :invalid_credentials failure. A token that fails verification never
|
|
106
|
+
# reaches uid, info, or the application.
|
|
107
|
+
def build_access_token
|
|
108
|
+
expected_nonce = session.delete(nonce_session_key)
|
|
109
|
+
super.tap do |token|
|
|
110
|
+
@id_token_claims = verify_id_token(token.response.parsed["id_token"], expected_nonce)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def verify_id_token(id_token, expected_nonce)
|
|
115
|
+
raise CallbackError.new(:invalid_id_token, "The token response carried no ID token") if id_token.to_s.empty?
|
|
116
|
+
|
|
117
|
+
claims, = ::JWT.decode(
|
|
118
|
+
id_token, nil, true,
|
|
119
|
+
algorithms: [ "RS256" ],
|
|
120
|
+
jwks: ->(loader_options) { signing_keys(**loader_options.slice(:kid_not_found)) },
|
|
121
|
+
iss: options.issuer, verify_iss: true,
|
|
122
|
+
aud: options.client_id, verify_aud: true,
|
|
123
|
+
required_claims: %w[sub exp iat],
|
|
124
|
+
verify_iat: true,
|
|
125
|
+
leeway: options.leeway
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
verify_authorized_party(claims)
|
|
129
|
+
verify_nonce(claims, expected_nonce)
|
|
130
|
+
claims
|
|
131
|
+
rescue ::JWT::DecodeError, ::OAuth2::Error, ::Faraday::Error, ::JSON::ParserError => e
|
|
132
|
+
raise CallbackError.new(:invalid_id_token, e.message)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# OpenID Connect Core 3.1.3.7: a token for several audiences must name
|
|
136
|
+
# this client as the party it was issued to.
|
|
137
|
+
def verify_authorized_party(claims)
|
|
138
|
+
audiences = Array(claims["aud"])
|
|
139
|
+
return if audiences.one? || claims["azp"] == options.client_id
|
|
140
|
+
|
|
141
|
+
raise CallbackError.new(:invalid_id_token, "The ID token was issued to another party")
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def verify_nonce(claims, expected_nonce)
|
|
145
|
+
return if expected_nonce && Rack::Utils.secure_compare(claims["nonce"].to_s, expected_nonce)
|
|
146
|
+
|
|
147
|
+
raise CallbackError.new(:invalid_id_token, "The ID token nonce did not match")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def signing_keys(kid_not_found: false)
|
|
151
|
+
key_set.fetch(kid_not_found: kid_not_found) do
|
|
152
|
+
client.request(:get, options.jwks_uri).parsed
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def key_set
|
|
157
|
+
KEY_SETS_LOCK.synchronize { KEY_SETS[options.jwks_uri] }
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def client_auth_method
|
|
161
|
+
method = options.client_auth_method&.to_sym
|
|
162
|
+
method || (options.client_secret.to_s.empty? ? :none : :basic)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# RFC 6749 2.3.1: both halves are form-encoded before Base64.
|
|
166
|
+
def basic_authorization
|
|
167
|
+
pair = [ options.client_id, options.client_secret ].map { |part| URI.encode_www_form_component(part.to_s) }.join(":")
|
|
168
|
+
"Basic #{[ pair ].pack("m0")}"
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def nonce_session_key
|
|
172
|
+
"omniauth.#{name}.nonce"
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
OmniAuth.config.add_camelization "openai", "OpenAI"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/omniauth/openai/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "omniauth-openai"
|
|
7
|
+
spec.version = OmniAuth::OpenAI::VERSION
|
|
8
|
+
spec.authors = [ "Ajay Krishnan" ]
|
|
9
|
+
spec.email = [ "omniauth-openai@ajay.to" ]
|
|
10
|
+
|
|
11
|
+
spec.summary = "OmniAuth strategy for Sign in with ChatGPT (OpenAI OpenID Connect)."
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
Adds "Sign in with ChatGPT" to Rack and Rails applications through
|
|
14
|
+
OmniAuth and Devise. Authorization code flow with PKCE and a nonce; the
|
|
15
|
+
ID token's signature, issuer, audience, expiry, and nonce are verified
|
|
16
|
+
against OpenAI's published keys before the application sees an identity.
|
|
17
|
+
Unofficial; not affiliated with or endorsed by OpenAI.
|
|
18
|
+
DESC
|
|
19
|
+
spec.homepage = "https://github.com/ajaynomics/omniauth-openai"
|
|
20
|
+
spec.license = "MIT"
|
|
21
|
+
spec.required_ruby_version = ">= 3.2.0"
|
|
22
|
+
|
|
23
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
24
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
25
|
+
spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues"
|
|
26
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
27
|
+
|
|
28
|
+
spec.files = Dir.glob("lib/**/*.rb") + %w[README.md LICENSE CHANGELOG.md omniauth-openai.gemspec]
|
|
29
|
+
spec.require_paths = [ "lib" ]
|
|
30
|
+
|
|
31
|
+
spec.add_dependency "jwt", ">= 2.7", "< 4"
|
|
32
|
+
spec.add_dependency "omniauth-oauth2", "~> 1.8"
|
|
33
|
+
|
|
34
|
+
spec.add_development_dependency "minitest", ">= 5.20"
|
|
35
|
+
spec.add_development_dependency "rack-test", "~> 2.1"
|
|
36
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
37
|
+
spec.add_development_dependency "webmock", "~> 3.20"
|
|
38
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: omniauth-openai
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ajay Krishnan
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: jwt
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.7'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '4'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '2.7'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '4'
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: omniauth-oauth2
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - "~>"
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '1.8'
|
|
39
|
+
type: :runtime
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - "~>"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '1.8'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: minitest
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '5.20'
|
|
53
|
+
type: :development
|
|
54
|
+
prerelease: false
|
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '5.20'
|
|
60
|
+
- !ruby/object:Gem::Dependency
|
|
61
|
+
name: rack-test
|
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
|
63
|
+
requirements:
|
|
64
|
+
- - "~>"
|
|
65
|
+
- !ruby/object:Gem::Version
|
|
66
|
+
version: '2.1'
|
|
67
|
+
type: :development
|
|
68
|
+
prerelease: false
|
|
69
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - "~>"
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '2.1'
|
|
74
|
+
- !ruby/object:Gem::Dependency
|
|
75
|
+
name: rake
|
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
|
77
|
+
requirements:
|
|
78
|
+
- - "~>"
|
|
79
|
+
- !ruby/object:Gem::Version
|
|
80
|
+
version: '13.0'
|
|
81
|
+
type: :development
|
|
82
|
+
prerelease: false
|
|
83
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - "~>"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '13.0'
|
|
88
|
+
- !ruby/object:Gem::Dependency
|
|
89
|
+
name: webmock
|
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "~>"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '3.20'
|
|
95
|
+
type: :development
|
|
96
|
+
prerelease: false
|
|
97
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - "~>"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '3.20'
|
|
102
|
+
description: |
|
|
103
|
+
Adds "Sign in with ChatGPT" to Rack and Rails applications through
|
|
104
|
+
OmniAuth and Devise. Authorization code flow with PKCE and a nonce; the
|
|
105
|
+
ID token's signature, issuer, audience, expiry, and nonce are verified
|
|
106
|
+
against OpenAI's published keys before the application sees an identity.
|
|
107
|
+
Unofficial; not affiliated with or endorsed by OpenAI.
|
|
108
|
+
email:
|
|
109
|
+
- omniauth-openai@ajay.to
|
|
110
|
+
executables: []
|
|
111
|
+
extensions: []
|
|
112
|
+
extra_rdoc_files: []
|
|
113
|
+
files:
|
|
114
|
+
- CHANGELOG.md
|
|
115
|
+
- LICENSE
|
|
116
|
+
- README.md
|
|
117
|
+
- lib/omniauth-openai.rb
|
|
118
|
+
- lib/omniauth/openai.rb
|
|
119
|
+
- lib/omniauth/openai/key_set.rb
|
|
120
|
+
- lib/omniauth/openai/version.rb
|
|
121
|
+
- lib/omniauth/strategies/openai.rb
|
|
122
|
+
- omniauth-openai.gemspec
|
|
123
|
+
homepage: https://github.com/ajaynomics/omniauth-openai
|
|
124
|
+
licenses:
|
|
125
|
+
- MIT
|
|
126
|
+
metadata:
|
|
127
|
+
source_code_uri: https://github.com/ajaynomics/omniauth-openai
|
|
128
|
+
changelog_uri: https://github.com/ajaynomics/omniauth-openai/blob/main/CHANGELOG.md
|
|
129
|
+
bug_tracker_uri: https://github.com/ajaynomics/omniauth-openai/issues
|
|
130
|
+
rubygems_mfa_required: 'true'
|
|
131
|
+
rdoc_options: []
|
|
132
|
+
require_paths:
|
|
133
|
+
- lib
|
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: 3.2.0
|
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - ">="
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '0'
|
|
144
|
+
requirements: []
|
|
145
|
+
rubygems_version: 4.0.16
|
|
146
|
+
specification_version: 4
|
|
147
|
+
summary: OmniAuth strategy for Sign in with ChatGPT (OpenAI OpenID Connect).
|
|
148
|
+
test_files: []
|