Compare commits

...

12 commits

Author SHA1 Message Date
7f4c34ff6a Oops, stop requiring a new password whenever AuthUser is changed
Ah right, I went and checked the Devise source code, and the default
implementation for `password_required?` is a bit trickier than I
expected:

```ruby
def password_required?
  !persisted? || !password.nil? || !password_confirmation.nil?
end
```

Looks like `super` does a good enough job here, though! (I'm actually
kinda surprised, I wasn't sure how Ruby's `super` rules worked, and
this isn't a subclass thing—or maybe it is, maybe the `devise` method
adds a mixin? Idk! But it does what I expect, so, great!)

So now, we require the password if 1) Devise doesn't see a UI reason
not to, *and* 2) the user isn't using OmniAuth (i.e. NeoPass).

This had caused a bug where it was impossible to use the Settings page
*without* changing your password! (The form says it's okay to leave it
blank, which stopped being true! But now it's fixed!)
2024-03-14 19:20:33 -07:00
3eeb5d1065 Actually create user from NeoPass authentication! <3 <3
Whew, exciting! Still done nothing against the live NeoPass server, but
we've got this fully working with the development server, it seems!
Wowie!!

This is all still hidden behind secret flags, so it's fine to deploy
live. (And it's not actually a problem if someone gets past to the
endpoints behind it, because we haven't actually set up real
credentials for our NeoPass client yet, so authentication will fail!)

Okay time to lie down lol.
2024-03-14 19:11:06 -07:00
31a11a04fa Read and customize the username reported by neopass-server
Okay, `sub` seems to be a pretty standard place for user identifiers.
Let's start with that assumption! I override the `oauth2-mock-server`'s
default of `johndoe` with `theneopetsteam`, just to be cute :3
2024-03-14 18:19:45 -07:00
9cbeee0acd Refactor to use OpenID Connect OmniAuth gem instead of plain OAuth2
Right, I didn't totally connect the dots that there's some OpenID
features in the mix here for how we expect to identify the user once
they authenticate. It requires looking up the provider's public key,
and validating the JWT they sent us. This gem does all that for us!

I don't actually know what a real NeoPass `id_token` looks like yet?
But I'll fill in some placeholder stuff for now, and use that for
initializing the account!
2024-03-14 18:11:40 -07:00
ffcfce2eb8 Use local-only HTTPS certs for the development neopass-server
I'm starting to work with the OpenID Connect stuff in NeoPass, and the
library I'm using for discovery doesn't seem to want to do it over a
plain `http://` connection. (I dug into the source files, and it just
actually is hardcoded to only work with HTTPS, as far as I can tell?)

So, I've added logic to `neopass-server` to try to make an HTTPS
certificate with the `mkcert` utility (if installed), which is a tool
that installs a root certificate authority on your local machine, then
helps you create certificates via that authority that will work only on
your local machine.

I think I'll also be able to remove the "main" server in front of the
backing server, because the library I'm using now will be able to
"discover" the auth and token endpoints, so it won't matter that our
local one uses different URLs than live NeoPass does? We'll find out!

I also remove `neopass-server` from the `Procfile`, because I think
it's a bit rude to have it auto-try to run `mkcert`. We could like,
make the process just a no-op in that case? But I think I'd prefer to
just run `neopass-server` by hand when I want it, for simplicity.
2024-03-14 18:01:54 -07:00
21bc4bcadc Fix neopass-server to respond correctly to /token requests
Hey nice, now we can actually get a round-trip auth success! This gets
us to the `Devise::OmniauthCallbacksController#neopass` success method,
so now we need to add our logic to actually login/signup!
2024-03-14 16:26:43 -07:00
f483722af4 NeoPass strategy interacts with dev NeoPass server, which is still WIP
In this change, we wire up a new NeoPass OAuth2 strategy for OmniAuth,
and hook up the "Log in with NeoPass" button to use it!

The authentication currently fails with `invalid_credentials`, and
shows the `owo` response we hardcoded into the NeoPass server's token
response. We need to finally follow up on the little `TODO` written in
there!
2024-03-14 16:13:31 -07:00
77057fe6a2 Add hidden "Log in with NeoPass" button, to placeholder login strategy
If you pass `?neopass=1` (or a secret value in production), you can see
the "Log in with NeoPass" button, which currently takes you to
OmniAuth's "developer" login page, where you can specify a name and
email and be redirected back. (All placeholder UI!)

We're gonna strip the whole developer strategy out pretty fast and
replace it with one that uses our NeoPass test server. This is just me
checking my understanding of the wiring!
2024-03-14 15:34:24 -07:00
08b1b9e83b Add OmniAuth plugin to AuthUser
This is setting us up for NeoPass, but first we're just gonna try stuff
with the "developer" strategy that's built in for testing, rather than
using the NeoPass dev server!
2024-03-14 15:06:13 -07:00
58e6b46b42 Split NeoPass dev server into main/backing servers 2024-03-14 15:06:13 -07:00
e04387a533 Also run neopass-server as part of bin/dev
Now, running `bin/dev` will also run `bin/neopass-server`, to make it
easier to Just Do It for testing NeoPass!
2024-03-14 15:06:13 -07:00
0db7d3f966 WIP: Add bin/neopass-server as test NeoPass server implementation
Just a little mock server for use in development! Not referenced by
anything yet.
2024-03-14 15:06:13 -07:00
39 changed files with 933 additions and 16 deletions

View file

@ -25,6 +25,9 @@ gem 'turbo-rails', '~> 2.0'
# For authentication.
gem 'devise', '~> 4.9', '>= 4.9.2'
gem 'devise-encryptable', '~> 0.2.0'
gem 'omniauth', '~> 2.1'
gem 'omniauth-rails_csrf_protection', '~> 1.0'
gem "omniauth_openid_connect", "~> 0.7.1"
# For pagination UI.
gem 'will_paginate', '~> 4.0'

View file

@ -83,6 +83,7 @@ GEM
tzinfo (~> 2.0)
addressable (2.8.6)
public_suffix (>= 2.0.2, < 6.0)
aes_key_wrap (1.1.0)
async (2.8.1)
console (~> 1.10)
fiber-annotation
@ -105,6 +106,7 @@ GEM
async
async-pool (0.4.0)
async (>= 1.25)
attr_required (1.0.2)
babel-source (5.8.35)
babel-transpiler (0.7.0)
babel-source (>= 4.0, < 6)
@ -112,6 +114,7 @@ GEM
base64 (0.2.0)
bcrypt (3.1.20)
bigdecimal (3.1.6)
bindata (2.5.0)
bindex (0.8.1)
bootsnap (1.18.3)
msgpack (~> 1.2)
@ -140,6 +143,8 @@ GEM
drb (2.2.0)
ruby2_keywords
e2mmap (0.1.0)
email_validator (2.2.4)
activemodel
erubi (1.12.0)
execjs (2.9.1)
falcon (0.43.0)
@ -155,6 +160,12 @@ GEM
process-metrics (~> 0.2.0)
protocol-rack (~> 0.1)
samovar (~> 2.1)
faraday (2.9.0)
faraday-net_http (>= 2.0, < 3.2)
faraday-follow_redirects (0.3.0)
faraday (>= 1, < 3)
faraday-net_http (3.1.0)
net-http
ffi (1.16.3)
fiber-annotation (0.2.0)
fiber-local (1.0.0)
@ -164,6 +175,7 @@ GEM
temple (>= 0.8.2)
thor
tilt
hashie (5.0.0)
http_accept_language (2.1.1)
httparty (0.21.0)
mini_mime (>= 1.0.0)
@ -178,6 +190,13 @@ GEM
jsbundling-rails (1.3.0)
railties (>= 6.0.0)
json (2.7.1)
json-jwt (1.16.6)
activesupport (>= 4.2)
aes_key_wrap
base64
bindata
faraday (~> 2.0)
faraday-follow_redirects
launchy (2.5.2)
addressable (~> 2.8)
letter_opener (1.9.0)
@ -201,6 +220,8 @@ GEM
multi_xml (0.6.0)
mutex_m (0.2.0)
mysql2 (0.5.6)
net-http (0.4.1)
uri
net-imap (0.4.10)
date
net-protocol
@ -214,6 +235,29 @@ GEM
nokogiri (1.16.2)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
omniauth (2.1.2)
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
omniauth-rails_csrf_protection (1.0.1)
actionpack (>= 4.2)
omniauth (~> 2.0)
omniauth_openid_connect (0.7.1)
omniauth (>= 1.9, < 3)
openid_connect (~> 2.2)
openid_connect (2.3.0)
activemodel
attr_required (>= 1.0.0)
email_validator
faraday (~> 2.0)
faraday-follow_redirects
json-jwt (>= 1.16)
mail
rack-oauth2 (~> 2.2)
swd (~> 2.0)
tzinfo
validate_url
webfinger (~> 2.0)
openssl (3.2.0)
orm_adapter (0.5.0)
parallel (1.24.0)
@ -239,6 +283,16 @@ GEM
rack (>= 1.0, < 4)
rack-mini-profiler (3.3.1)
rack (>= 1.2.0)
rack-oauth2 (2.2.1)
activesupport
attr_required
faraday (~> 2.0)
faraday-follow_redirects
json-jwt (>= 1.11.0)
rack (>= 2.1.0)
rack-protection (4.0.0)
base64 (>= 0.1.0)
rack (>= 3.0.0, < 4)
rack-session (2.0.0)
rack (>= 3.0.0)
rack-test (2.1.0)
@ -331,6 +385,11 @@ GEM
mini_portile2 (~> 2.8.0)
stackprof (0.2.26)
stringio (3.1.0)
swd (2.0.3)
activesupport (>= 3)
attr_required (>= 0.0.5)
faraday (~> 2.0)
faraday-follow_redirects
sync (0.5.0)
temple (0.10.3)
terser (1.2.0)
@ -347,6 +406,10 @@ GEM
railties (>= 6.0.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
uri (0.13.0)
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.2.1)
@ -354,6 +417,10 @@ GEM
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webfinger (2.1.3)
activesupport
faraday (~> 2.0)
faraday-follow_redirects
webrick (1.8.1)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
@ -382,6 +449,9 @@ DEPENDENCIES
memory_profiler (~> 1.0)
mysql2 (~> 0.5.5)
nokogiri (~> 1.15, >= 1.15.3)
omniauth (~> 2.1)
omniauth-rails_csrf_protection (~> 1.0)
omniauth_openid_connect (~> 0.7.1)
parallel (~> 1.23)
rack-attack (~> 6.7)
rack-mini-profiler (~> 3.1)

View file

@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base
before_action :set_locale
before_action :configure_permitted_parameters, if: :devise_controller?
before_action :check_neopass_access, if: :devise_controller?
before_action :save_return_to_path,
if: ->(c) { c.controller_name == 'sessions' && c.action_name == 'new' }
@ -87,6 +88,12 @@ class ApplicationController < ActionController::Base
devise_parameter_sanitizer.permit(:account_update, keys: [:email])
end
def check_neopass_access
@can_use_neopass = (
params[:neopass] == Rails.configuration.neopass_access_secret
)
end
def save_return_to_path
if params[:return_to]
Rails.logger.debug "Saving return_to path: #{params[:return_to].inspect}"

View file

@ -0,0 +1,48 @@
class Devise::OmniauthCallbacksController < ApplicationController
rescue_from AuthUser::MissingAuthInfoError, with: :missing_auth_info
rescue_from ActiveRecord::RecordInvalid, with: :validation_failed
def neopass
@auth_user = AuthUser.from_omniauth(request.env["omniauth.auth"])
if @auth_user.previously_new_record?
flash[:notice] =
"Welcome to Dress to Impress, #{@auth_user.name}! We've set up a DTI " +
"account for you. Click Settings in the top right to learn more, or " +
"just go ahead and get started!"
else
flash[:notice] = "Welcome back, #{@auth_user.name}! 💖"
end
sign_in_and_redirect @auth_user, event: :authentication
end
def failure
flash[:warning] =
"Hrm, something went wrong in our connection to NeoPass, sorry! " +
"Maybe wait a moment and try again?"
redirect_to new_auth_user_session_path
end
def missing_auth_info(error)
Sentry.capture_exception error
Rails.logger.error error.full_message
flash[:warning] =
"Hrm, our connection with NeoPass didn't return the information we " +
"usually expect, sorry! If this keeps happening, please email me at " +
"matchu@openneo.net so I can help investigate!"
redirect_to new_auth_user_session_path
end
def validation_failed(error)
Sentry.capture_exception error
Rails.logger.error error.full_message
flash[:warning] =
"Hrm, the connection with NeoPass worked, but we had trouble saving " +
"your account, sorry! If this keeps happening, please email me at " +
"matchu@openneo.net so I can help investigate!"
redirect_to new_auth_user_session_path
end
end

View file

@ -2,7 +2,8 @@ class AuthUser < AuthRecord
self.table_name = 'users'
devise :database_authenticatable, :encryptable, :registerable, :validatable,
:rememberable, :trackable, :recoverable
:rememberable, :trackable, :recoverable, :omniauthable,
omniauth_providers: [:neopass]
validates :name, presence: true, uniqueness: {case_sensitive: false},
length: {maximum: 20}
@ -26,4 +27,59 @@ class AuthUser < AuthRecord
user.name = name
user.save!
end
def uses_omniauth?
provider? && uid?
end
def email_required?
!uses_omniauth?
end
def password_required?
super && !uses_omniauth?
end
def self.from_omniauth(auth)
raise MissingAuthInfoError, "Username missing" if auth.uid.blank?
raise MissingAuthInfoError, "Email missing" if auth.info.email.blank?
transaction do
find_or_create_by!(provider: auth.provider, uid: auth.uid) do |user|
# Use the Neopets username if possible, or a unique username if not.
dti_username = build_unique_username_like(auth.uid)
user.name = dti_username
# Copy the email address from their Neopets account to their DTI
# account, unless they already have a DTI account with this email, in
# which case, ignore it. (It's primarily for their own convenience with
# password recovery!)
email_exists = AuthUser.where(email: auth.info.email).exists?
user.email = auth.info.email unless email_exists
end
end
end
def self.build_unique_username_like(name)
name_query = sanitize_sql_like(name) + "%"
similar_names = where("name LIKE ?", name_query).pluck(:name)
# Use the given name itself, if we can.
return name unless similar_names.include?(name)
# If not, try appending "-neopass".
return "#{name}-neopass" unless similar_names.include?("#{name}-neopass")
# After that, try appending "-neopass-1", "-neopass-2", etc, until a
# unique name arises. (We don't expect this to happen basically ever, but
# it's nice to have a guarantee!)
max = similar_names.size + 1
candidates = (1..max).map { |n| "#{name}-neopass-#{n}"}
numerical_name = candidates.find { |name| !similar_names.include?(name) }
return numerical_name unless numerical_name.nil?
raise "Failed to build unique username (shouldn't be possible?)"
end
class MissingAuthInfoError < ArgumentError;end
end

View file

@ -1,5 +1,12 @@
<h2>Log in</h2>
<% if @can_use_neopass %>
<%= button_to "Log in with NeoPass",
auth_user_neopass_omniauth_authorize_path,
data: {turbo: false} # Turbo can't handle this redirect!
%>
<% end %>
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
<div class="field">
<%= f.label :name, 'Username' %><br />

View file

@ -17,9 +17,3 @@
<%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %>
<%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %><br />
<% end %>
<%- if devise_mapping.omniauthable? %>
<%- resource_class.omniauth_providers.each do |provider| %>
<%= button_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider), data: { turbo: false } %><br />
<% end %>
<% end %>

111
bin/neopass-server Executable file
View file

@ -0,0 +1,111 @@
#!/usr/bin/env node
/**
* A test NeoPass server! This is a very lean, hacky implementation, designed
* to just see the basic OAuth interactions Work At All.
*
* This server is an `oauth2-mock-server` instance that's easy to spin up and
* have perform OAuth for us. We give it a hardcoded development-only key, and
* it just auto-grants permissions!
*
* It slightly differs from the NeoPass spec, in that it uses different paths
* for its endpoints, but that's okay: DTI will use OpenID's "discovery"
* feature to discover those endpoints via a single well-known path, without
* needing them hardcoded.
*/
const fs = require("node:fs/promises");
const pathLib = require("node:path");
const { spawn } = require("node:child_process");
const urlLib = require("node:url");
const { OAuth2Server } = require("oauth2-mock-server");
const express = require("express");
// This is the Neopets username and email we'll report back to DTI when you
// authenticate through here.
const USERNAME = "test";
const EMAIL = "theneopetsteam@neopets.com";
const certPath = pathLib.join(__dirname, "..", "tmp", "localhost.pem");
const keyPath = pathLib.join(__dirname, "..", "tmp", "localhost-key.pem");
async function fileExists(path) {
try {
await fs.stat(path);
} catch (error) {
if (error.code === "ENOENT") {
return false;
}
throw error;
}
return true;
}
async function ensureCertsExist() {
if (!(await fileExists(certPath)) || !(await fileExists(keyPath))) {
console.log(
"Using mkcert to create localhost.pem and localhost-key.pem in " +
"the Rails tmp dir, to serve over HTTPS.",
);
const mkcertProc = spawn(
"mkcert",
["-cert-file", certPath, "-key-file", keyPath, "localhost"],
{ stdio: ["ignore", process.stdout, process.stderr] },
);
// Wait for the process to finish, raising an error if it fails.
await new Promise((resolve, reject) => {
mkcertProc.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error(`mkcert returned status ${code}`));
}
});
mkcertProc.on("error", (error) => {
reject(error);
});
});
}
}
async function startServer(port) {
const server = new OAuth2Server(keyPath, certPath);
await server.issuer.keys.add({
// A key we generated for the NeoPass test server. It's okay for its
// "secret" info to be here, because it's for development only!
kid: "neopass-server-DEVLOPMENT-ONLY-NOT-FOR-PRODUCTION",
p: "50btwsJlPbGLUFnCSBZzddyMX_oRQ8nz4lMrpAd4umPLqMUmS0NbBZNf6DI7s8PkRUxtV8KdvZh3OYWavFnbk55GDG4Y_J_wA4XlHU3d5KIfSNaIdbtVp4CFOq1lovho4sYX_26vcGgYb2Azeg7nz_gDpqNmIdJdKuZxzsrboK8",
kty: "RSA",
q: "xha0i9_lbOMQhmmni02Dtpocil26GI7W8xbOFyOvceBCRNf-XOA_-W_Xk9ItJRHnAWM1TML36PN0l864d4QAXbBo64FHu2cjdFKnXJNliJaPcOPAMQB_D8GSylU1gTwSpP_vVe8t232LeF1oBwbOoBIS-6NsOpLmL8Sezv6Fpac",
d: "WSUNeEd_EyaELK7wqT6GJJK_RfYjaE5h6USe9rD9cd_tQE2PaZWXMyZ4OCk5Z5hdG2ryZY7NYsOI2CPs8HCFBqMoKd0z0A0EgB8Dq2fe_-t5Rm0Zq1ZnI5tnBcZeQmw0hDT98Wg00FA53SSUqfnOgI_VuLvquM6f18_XQOKRRfTcwh1a4teDAH0g0s8FVOS5DANtg71mTdq5fEkWmQMD3qKC6SNrx3WXXHezDs0MWdeFqn9Dg7gssSqB7PnqB-hlC_fHnu4gm9nDqPTMzsJC2i8d3adm0AeORRCulGLe7hU-_TgTbZzgIYCgOK_asaewW-6Qk9qFj-J4djBaKIee-Q",
e: "AQAB",
use: "sig",
qi: "WNiwCcAk2x7e0KvuupL2DNU-JUjLEF9Onee5T9u9ihbgGSDIyP04_96TzCIK3wsY6lct64oOo0Er-z5cf_5eOBPD3n0eEL-JuKIgn0mEKrazJOnGQzlyeZPzk4dUO2J7D42ObopfYsoBIcJx-Y_43a6WORDMGSVCiURmKavTHUU",
dp: "p1_wj-Npq3VDElpzPQJqeuCrAoaSWhHcm21_hs0VdSbl6_UJ2qwbQnS-kudPx7A8El7WPw4MZHrjxdBIBImvXCzOGw7OrHz_ET2ka0nADUe7BlakGTgDLB7ZzHZSuNe36G5eTbCH7PyYunnPp0UERMEDu2RDdLSuUm7F7FdpDOc",
alg: "RS256",
dq: "purLCHKKKM7NRfYRsFiI_H2wPwfroHX8uqokz2rKk_Kc5NX9CNYOEmokBfO9BtenCIxIhX5k2G8NeD5BQrSAenIEdy5g-5FVVtevH1s023vDMyU29hOs_eHnh4d1poiwTUk8q_T3d1S7CZnr5r_drRSN2m1C7biLLwVHrLTceVE",
n: "svVfGU4NGcfBCmQiIOW5uzg5SAN2CWSIQSstnhqZoCdjy5OoKpKVR8O9TbDvxixrvkFyAav90Q0Xse8iFTcjfCKuqINYiuYMXhCvfBlc_DVVOQca9pMpN03LaDofd5Ll4_BFTtt1nSPahwWU7xDM-Bkkh_TcS2qS4N2xbpEGi0q0ZkrJN4WyiDBC2k9WbK-YHr4Rj4JKypFVSeBIrjxVPmlPzgfqlLGGIB0l92SnJDXDMlkWcCCTyLgqSBM04nkxGDSykq_ei76qCdRd7b10wMBaoS9DeBThAyHpur2LoPdH3gxbcwoWExi-jPlNP1LdKVZD8b95OY3CRyMAAMGdKQ",
});
server.service.on("beforeTokenSigning", (token) => {
token.payload.sub = USERNAME;
});
server.service.on("beforeUserinfo", (userInfoResponse) => {
userInfoResponse.body.email = EMAIL;
});
await server.start(port, "localhost");
console.log(`Started NeoPass development server at: ${server.issuer.url}`);
}
async function main() {
await ensureCertsExist();
await startServer(8585);
}
main().catch((error) => {
console.error(error);
});

View file

@ -1 +1 @@
b0WTE8+0LBv1VLmSEw0wmJJmjFctFHF9oIM4xKHJsWapYBppphc3Lvx+cCvIZRq9+K0KSR+ugkJKd+c8UqW0RxanjDFm4o7aZpxpI4WuKzqjcYhnBHXzx5HFdlLkXqPoU3NgSPvcPVPEk5AW5jPSCB7MBQOr4S5l1/mybJX4m0AlhoyXVAaPGOb0vZ3skk0eLeFn60aRq4TTrG7xiSkxfH5X0cOjPpNBmN/s03A8z72oYSy9Oa28V0GjFkXhxN9jnOpZJ/P3RBRovGInpcCFcle/EIUl/O+l9VP1tfR+szhBSbCXA2/wYedaZIvTuwF6iyCYz2k3lgD9qr3iQ+mACxoa/a2PE6grsSHD/FTzIcQPxmivoUFiRMtBdrJHCX6JUboprWG+y5SrQYFtJ0JkjpfnJYb4sqszMyQkvOP+GvQQ++CQFYHlD1fBweCKwFD6GNevj2AuCdxFGnCUn8Vjkj++qk8o63LsbO++6iFQXZnVMFC+7Cgq7Xnp74oDs1Jg9UD8uYHZcKQGcJv7y86BSAyuMb6lwlQqUhdfG7k/PpN0f2yC6JLAqKZ8VChTVjn62TIXkTenPY6ZsKwk9wstNAjVukCCGKXDt4uaRIurQnBT2lkTHdqnMIiYSEqBlvc/yAuDyxyXqndMvYhDsdetNdnIxATRkpA+mU4CHlgcv30J5l3obLN8UA0oHPL9lTMWU63E4JyQTwVEHGLy+J8TMZgJX1imCCzCzWL7ytdDy7HdZFXrVPxj+z95/PH//2NcvuAdwbnKX7Pk0ujEBwSnkmiw1UZgl5VT5G2yLwYqFY1VgyINBN42ynFFRfiNjk9Q9bIHk3i2bQnDAw9EtAuKbpdJ9IXboTAOHOIZpanW6RiIfIGk+MM5GLKOSthv6XCzhLue7P//tprHT85yE1vnhLNeCGg1EtgDlM9y--xXvKjFS/mwn+dHoa--u457xDa2Q8CLtVbYkL6pig==
p001YS2L7h/DJ9mWOUxcWD/wBNXQ41BOsN9vjnW9QWtECNNzMjRsPVZ8vkOXmRt5mHAOmvzAUhRbKmWE0PaUHMpJAVlct1mCpXlE2rHwKESuZ4S1tJFiSCfu0Uu97Nbw3kQScMJ9cB801QPpjDq5FxoCEz4XtuCEc8nuh23Ni8I77Jw9NG4VfiFFQHFhNk8xhAoAIiYekEoliRYJc1osmzSb8FcDlIV/GompPN4G2EaBcFvX1CMP2F2AYHR8EVvaeTmIv0GsdLL8NnEnsuqd+GZMljIr346aOSOzmUC76rgubpEdqG4mBZzzUjQxvXqnuawOmbQWAbDkDcSoZYwVhc2/QmR3VtFtV/YaBX27h2cp9Ef14FgpK7y1KO1vE7row8lxq4rwtZaGT0nbXf//4gpdDdrRKEOJRjwi5l+ydPgfa4aHQohFZ+4a7App3N5dovG1/c9W/a4T/7i5aBxYeiCKD6+byss74jJaqZ34eNxVQOZxoi+HfHPQwMRwsGVxKzGJaGLijwjuYq5iAdSBiMY2WouF17gNJKXgEILFvy/xAyHnUDM3K/traeC7ULRztdGGxHHIC2D26+s5mS5zI9OAM2lXI6ms4jKEui/BYKuQ/sHpB4Cg7wdk0JQ5SPW/8b35oW6Cuz31NrzUju5WXtXtGvlMPBKUWz8q8wIGEtM5Bc/5ASDS9Ag8J8seW2gprab2Ja7apUXPBUGuR5aU6JKLGNpAM/vV9lavgrm/rvXNlkPvgoULpBhtZI5EfuiSncke7NCuJUEw4fZr5KeRze5U7qyk1Jg+Fz2nQGvOn0T1PDvezN5yT+b/0YZnyTI9zmhur4KY4Z9OZgsMTG073qkdZF6y3qeWKHpWrhXKWc8i/CAVsUKrWQpSDWDhvzhhanR936OEoqqpKoGunsba1fh5oOdrTBiFnH+MfI/IAh7tjUjcQx6bu8/1rdaZ7omBdaeDx36SekoOqndCBgrMX0mri5hoG2LIVA==--VLKM05ugRRSrks6H--/nICajJes+PjNkh9lyRi0Q==

View file

@ -116,4 +116,14 @@ Rails.application.configure do
# When developing the `public_data:commit` command, save to the local `tmp`
# folder. (In production, we keep this in a long-term location instead!)
config.public_data_root = Rails.root / "tmp" / "public_data"
# To see NeoPass features, add ?neopass=1 to relevant pages.
config.neopass_access_secret = "1"
# Use the local NeoPass development server.
config.neopass_origin = "https://localhost:8585"
# Set the NeoPass redirect callback URL.
config.neopass_redirect_uri =
"http://localhost:3000/auth_users/auth/neopass/callback"
end

View file

@ -134,4 +134,15 @@ Rails.application.configure do
# Save our public data exports in `public/public-data`. (This should be
# symlinked to a shared folder persisted across all versions.)
config.public_data_root = Rails.root / "public" / "public-data"
# To see NeoPass features, add ?neopass=<SECRET> to relevant pages.
config.neopass_access_secret =
Rails.application.credentials.neopass.access_secret
# Use the live NeoPass production server.
config.neopass_origin = "https://oidc.neopets.com"
# Set the NeoPass redirect callback URL.
config.neopass_redirect_uri =
"https://impress.openneo.net/auth_users/auth/neopass/callback"
end

View file

@ -71,4 +71,14 @@ Rails.application.configure do
# we keep this in a long-term location instead!)
config.neopets_media_archive_root = Rails.root / "tmp" /
"neopets_media_archive" / "test"
# To see NeoPass features, add ?neopass=1 to relevant pages.
config.neopass_access_secret = "1"
# Use the local NeoPass development server.
config.neopass_origin = "https://localhost:8585"
# Set the NeoPass redirect callback URL.
config.neopass_redirect_uri =
"http://localhost:3000/auth_users/auth/neopass/callback"
end

View file

@ -273,7 +273,21 @@ Devise.setup do |config|
# ==> OmniAuth
# Add a new OmniAuth provider. Check the wiki for more information on setting
# up on your models and hooks.
# config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo'
config.omniauth :openid_connect, {
name: :neopass,
scope: [:openid, :email, :profile],
response_type: :code,
issuer: Rails.configuration.neopass_origin,
discovery: true,
client_options: {
identifier: "DTI-TODO",
secret: "DTI-TODO",
redirect_uri: Rails.configuration.neopass_redirect_uri,
},
}
# Output OmniAuth debug info to the server logs in development
OmniAuth.config.logger = Rails.logger if Rails.env.development?
# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or

View file

@ -15,7 +15,7 @@
# inflect.acronym "RESTful"
# end
# Teach Zeitwerk that `RocketAMF` is what to expect in `lib/rocketamf`.
ActiveSupport::Inflector.inflections(:en) do |inflect|
# Teach Zeitwerk that `RocketAMF` is what to expect in `lib/rocketamf`.
inflect.acronym "RocketAMF"
end

View file

@ -0,0 +1,6 @@
class AddOmniauthFieldsToUsers < ActiveRecord::Migration[7.1]
def change
add_column :users, :provider, :string
add_column :users, :uid, :string
end
end

View file

@ -0,0 +1,7 @@
class AllowNullEmailAndPasswordForUsers < ActiveRecord::Migration[7.1]
def change
change_column_null :users, :email, true
change_column_null :users, :encrypted_password, true
change_column_null :users, :password_salt, true
end
end

View file

@ -10,12 +10,12 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2023_08_07_005748) do
ActiveRecord::Schema[7.1].define(version: 2024_03_15_020053) do
create_table "users", id: { type: :integer, unsigned: true }, charset: "utf8mb3", collation: "utf8mb3_general_ci", force: :cascade do |t|
t.string "name", limit: 20, null: false
t.string "encrypted_password", limit: 64, null: false
t.string "email", limit: 50, null: false
t.string "password_salt", limit: 32, null: false
t.string "encrypted_password", limit: 64
t.string "email", limit: 50
t.string "password_salt", limit: 32
t.string "reset_password_token"
t.integer "sign_in_count", default: 0
t.datetime "current_sign_in_at", precision: nil
@ -29,6 +29,8 @@ ActiveRecord::Schema[7.1].define(version: 2023_08_07_005748) do
t.datetime "updated_at", precision: nil
t.datetime "reset_password_sent_at", precision: nil
t.datetime "remember_created_at"
t.string "provider"
t.string "uid"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
t.index ["unlock_token"], name: "index_users_on_unlock_token", unique: true

View file

@ -35,7 +35,10 @@
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"express": "^4.18.3",
"husky": "^8.0.3",
"node-fetch": "^3.3.2",
"oauth2-mock-server": "^7.1.1",
"prettier": "^3.0.3",
"typescript": "^5.2.2"
},

BIN
vendor/cache/aes_key_wrap-1.1.0.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/attr_required-1.0.2.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/bindata-2.5.0.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/email_validator-2.2.4.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/faraday-2.9.0.gem vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
vendor/cache/faraday-net_http-3.1.0.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/hashie-5.0.0.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/json-jwt-1.16.6.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/net-http-0.4.1.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/omniauth-2.1.2.gem vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
vendor/cache/openid_connect-2.3.0.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/rack-oauth2-2.2.1.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/rack-protection-4.0.0.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/swd-2.0.3.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/uri-0.13.0.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/validate_url-1.0.15.gem vendored Normal file

Binary file not shown.

BIN
vendor/cache/webfinger-2.1.3.gem vendored Normal file

Binary file not shown.

562
yarn.lock
View file

@ -1837,6 +1837,16 @@ __metadata:
languageName: node
linkType: hard
"accepts@npm:~1.3.8":
version: 1.3.8
resolution: "accepts@npm:1.3.8"
dependencies:
mime-types: "npm:~2.1.34"
negotiator: "npm:0.6.3"
checksum: 3a35c5f5586cfb9a21163ca47a5f77ac34fa8ceb5d17d2fa2c0d81f41cbd7f8c6fa52c77e2c039acc0f4d09e71abdc51144246900f6bef5e3c4b333f77d89362
languageName: node
linkType: hard
"acorn-jsx@npm:^5.3.2":
version: 5.3.2
resolution: "acorn-jsx@npm:5.3.2"
@ -1967,6 +1977,13 @@ __metadata:
languageName: node
linkType: hard
"array-flatten@npm:1.1.1":
version: 1.1.1
resolution: "array-flatten@npm:1.1.1"
checksum: 806966c8abb2f858b08f5324d9d18d7737480610f3bd5d3498aaae6eb5efdc501a884ba019c9b4a8f02ff67002058749d05548fd42fa8643f02c9c7f22198b91
languageName: node
linkType: hard
"array-includes@npm:^3.1.6, array-includes@npm:^3.1.7":
version: 3.1.7
resolution: "array-includes@npm:3.1.7"
@ -2096,6 +2113,35 @@ __metadata:
languageName: node
linkType: hard
"basic-auth@npm:^2.0.1":
version: 2.0.1
resolution: "basic-auth@npm:2.0.1"
dependencies:
safe-buffer: "npm:5.1.2"
checksum: 05f56db3a0fc31c89c86b605231e32ee143fb6ae38dc60616bc0970ae6a0f034172def99e69d3aed0e2c9e7cac84e2d63bc51a0b5ff6ab5fc8808cc8b29923c1
languageName: node
linkType: hard
"body-parser@npm:1.20.2":
version: 1.20.2
resolution: "body-parser@npm:1.20.2"
dependencies:
bytes: "npm:3.1.2"
content-type: "npm:~1.0.5"
debug: "npm:2.6.9"
depd: "npm:2.0.0"
destroy: "npm:1.2.0"
http-errors: "npm:2.0.0"
iconv-lite: "npm:0.4.24"
on-finished: "npm:2.4.1"
qs: "npm:6.11.0"
raw-body: "npm:2.5.2"
type-is: "npm:~1.6.18"
unpipe: "npm:1.0.0"
checksum: 06f1438fff388a2e2354c96aa3ea8147b79bfcb1262dfcc2aae68ec13723d01d5781680657b74e9f83c808266d5baf52804032fbde2b7382b89bd8cdb273ace9
languageName: node
linkType: hard
"brace-expansion@npm:^1.1.7":
version: 1.1.11
resolution: "brace-expansion@npm:1.1.11"
@ -2115,6 +2161,13 @@ __metadata:
languageName: node
linkType: hard
"bytes@npm:3.1.2":
version: 3.1.2
resolution: "bytes@npm:3.1.2"
checksum: 76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e
languageName: node
linkType: hard
"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.4, call-bind@npm:^1.0.5":
version: 1.0.5
resolution: "call-bind@npm:1.0.5"
@ -2200,6 +2253,22 @@ __metadata:
languageName: node
linkType: hard
"content-disposition@npm:0.5.4":
version: 0.5.4
resolution: "content-disposition@npm:0.5.4"
dependencies:
safe-buffer: "npm:5.2.1"
checksum: bac0316ebfeacb8f381b38285dc691c9939bf0a78b0b7c2d5758acadad242d04783cee5337ba7d12a565a19075af1b3c11c728e1e4946de73c6ff7ce45f3f1bb
languageName: node
linkType: hard
"content-type@npm:~1.0.4, content-type@npm:~1.0.5":
version: 1.0.5
resolution: "content-type@npm:1.0.5"
checksum: b76ebed15c000aee4678c3707e0860cb6abd4e680a598c0a26e17f0bfae723ec9cc2802f0ff1bc6e4d80603719010431d2231018373d4dde10f9ccff9dadf5af
languageName: node
linkType: hard
"convert-source-map@npm:^1.5.0":
version: 1.9.0
resolution: "convert-source-map@npm:1.9.0"
@ -2207,6 +2276,20 @@ __metadata:
languageName: node
linkType: hard
"cookie-signature@npm:1.0.6":
version: 1.0.6
resolution: "cookie-signature@npm:1.0.6"
checksum: b36fd0d4e3fef8456915fcf7742e58fbfcc12a17a018e0eb9501c9d5ef6893b596466f03b0564b81af29ff2538fd0aa4b9d54fe5ccbfb4c90ea50ad29fe2d221
languageName: node
linkType: hard
"cookie@npm:0.5.0":
version: 0.5.0
resolution: "cookie@npm:0.5.0"
checksum: c01ca3ef8d7b8187bae434434582288681273b5a9ed27521d4d7f9f7928fe0c920df0decd9f9d3bbd2d14ac432b8c8cf42b98b3bdd5bfe0e6edddeebebe8b61d
languageName: node
linkType: hard
"copy-to-clipboard@npm:3.3.1":
version: 3.3.1
resolution: "copy-to-clipboard@npm:3.3.1"
@ -2216,6 +2299,16 @@ __metadata:
languageName: node
linkType: hard
"cors@npm:^2.8.5":
version: 2.8.5
resolution: "cors@npm:2.8.5"
dependencies:
object-assign: "npm:^4"
vary: "npm:^1"
checksum: 373702b7999409922da80de4a61938aabba6929aea5b6fd9096fefb9e8342f626c0ebd7507b0e8b0b311380744cc985f27edebc0a26e0ddb784b54e1085de761
languageName: node
linkType: hard
"cosmiconfig@npm:^7.0.0":
version: 7.1.0
resolution: "cosmiconfig@npm:7.1.0"
@ -2270,6 +2363,22 @@ __metadata:
languageName: node
linkType: hard
"data-uri-to-buffer@npm:^4.0.0":
version: 4.0.1
resolution: "data-uri-to-buffer@npm:4.0.1"
checksum: 20a6b93107597530d71d4cb285acee17f66bcdfc03fd81040921a81252f19db27588d87fc8fc69e1950c55cfb0bf8ae40d0e5e21d907230813eb5d5a7f9eb45b
languageName: node
linkType: hard
"debug@npm:2.6.9":
version: 2.6.9
resolution: "debug@npm:2.6.9"
dependencies:
ms: "npm:2.0.0"
checksum: 121908fb839f7801180b69a7e218a40b5a0b718813b886b7d6bdb82001b931c938e2941d1e4450f33a1b1df1da653f5f7a0440c197f29fbf8a6e9d45ff6ef589
languageName: node
linkType: hard
"debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.4":
version: 4.3.4
resolution: "debug@npm:4.3.4"
@ -2311,6 +2420,13 @@ __metadata:
languageName: node
linkType: hard
"depd@npm:2.0.0":
version: 2.0.0
resolution: "depd@npm:2.0.0"
checksum: 58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c
languageName: node
linkType: hard
"dequal@npm:^2.0.3":
version: 2.0.3
resolution: "dequal@npm:2.0.3"
@ -2318,6 +2434,13 @@ __metadata:
languageName: node
linkType: hard
"destroy@npm:1.2.0":
version: 1.2.0
resolution: "destroy@npm:1.2.0"
checksum: bd7633942f57418f5a3b80d5cb53898127bcf53e24cdf5d5f4396be471417671f0fee48a4ebe9a1e9defbde2a31280011af58a57e090ff822f589b443ed4e643
languageName: node
linkType: hard
"detect-node-es@npm:^1.1.0":
version: 1.1.0
resolution: "detect-node-es@npm:1.1.0"
@ -2369,6 +2492,13 @@ __metadata:
languageName: node
linkType: hard
"ee-first@npm:1.1.1":
version: 1.1.1
resolution: "ee-first@npm:1.1.1"
checksum: b5bb125ee93161bc16bfe6e56c6b04de5ad2aa44234d8f644813cc95d861a6910903132b05093706de2b706599367c4130eb6d170f6b46895686b95f87d017b7
languageName: node
linkType: hard
"emoji-regex@npm:^9.2.2":
version: 9.2.2
resolution: "emoji-regex@npm:9.2.2"
@ -2376,6 +2506,13 @@ __metadata:
languageName: node
linkType: hard
"encodeurl@npm:~1.0.2":
version: 1.0.2
resolution: "encodeurl@npm:1.0.2"
checksum: f6c2387379a9e7c1156c1c3d4f9cb7bb11cf16dd4c1682e1f6746512564b053df5781029b6061296832b59fb22f459dbe250386d217c2f6e203601abb2ee0bec
languageName: node
linkType: hard
"error-ex@npm:^1.3.1":
version: 1.3.2
resolution: "error-ex@npm:1.3.2"
@ -2569,6 +2706,13 @@ __metadata:
languageName: node
linkType: hard
"escape-html@npm:~1.0.3":
version: 1.0.3
resolution: "escape-html@npm:1.0.3"
checksum: 524c739d776b36c3d29fa08a22e03e8824e3b2fd57500e5e44ecf3cc4707c34c60f9ca0781c0e33d191f2991161504c295e98f68c78fe7baa6e57081ec6ac0a3
languageName: node
linkType: hard
"escape-string-regexp@npm:^1.0.5":
version: 1.0.5
resolution: "escape-string-regexp@npm:1.0.5"
@ -2752,6 +2896,52 @@ __metadata:
languageName: node
linkType: hard
"etag@npm:~1.8.1":
version: 1.8.1
resolution: "etag@npm:1.8.1"
checksum: 12be11ef62fb9817314d790089a0a49fae4e1b50594135dcb8076312b7d7e470884b5100d249b28c18581b7fd52f8b485689ffae22a11ed9ec17377a33a08f84
languageName: node
linkType: hard
"express@npm:^4.18.2, express@npm:^4.18.3":
version: 4.18.3
resolution: "express@npm:4.18.3"
dependencies:
accepts: "npm:~1.3.8"
array-flatten: "npm:1.1.1"
body-parser: "npm:1.20.2"
content-disposition: "npm:0.5.4"
content-type: "npm:~1.0.4"
cookie: "npm:0.5.0"
cookie-signature: "npm:1.0.6"
debug: "npm:2.6.9"
depd: "npm:2.0.0"
encodeurl: "npm:~1.0.2"
escape-html: "npm:~1.0.3"
etag: "npm:~1.8.1"
finalhandler: "npm:1.2.0"
fresh: "npm:0.5.2"
http-errors: "npm:2.0.0"
merge-descriptors: "npm:1.0.1"
methods: "npm:~1.1.2"
on-finished: "npm:2.4.1"
parseurl: "npm:~1.3.3"
path-to-regexp: "npm:0.1.7"
proxy-addr: "npm:~2.0.7"
qs: "npm:6.11.0"
range-parser: "npm:~1.2.1"
safe-buffer: "npm:5.2.1"
send: "npm:0.18.0"
serve-static: "npm:1.15.0"
setprototypeof: "npm:1.2.0"
statuses: "npm:2.0.1"
type-is: "npm:~1.6.18"
utils-merge: "npm:1.0.1"
vary: "npm:~1.1.2"
checksum: 0b9eeafbac549e3c67d92d083bf1773e358359f41ad142b92121935c6348d29079b75054555b3f62de39263fffc8ba06898b09fdd3e213e28e714c03c5d9f44c
languageName: node
linkType: hard
"fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3":
version: 3.1.3
resolution: "fast-deep-equal@npm:3.1.3"
@ -2795,6 +2985,16 @@ __metadata:
languageName: node
linkType: hard
"fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4":
version: 3.2.0
resolution: "fetch-blob@npm:3.2.0"
dependencies:
node-domexception: "npm:^1.0.0"
web-streams-polyfill: "npm:^3.0.3"
checksum: 60054bf47bfa10fb0ba6cb7742acec2f37c1f56344f79a70bb8b1c48d77675927c720ff3191fa546410a0442c998d27ab05e9144c32d530d8a52fbe68f843b69
languageName: node
linkType: hard
"file-entry-cache@npm:^6.0.1":
version: 6.0.1
resolution: "file-entry-cache@npm:6.0.1"
@ -2813,6 +3013,21 @@ __metadata:
languageName: node
linkType: hard
"finalhandler@npm:1.2.0":
version: 1.2.0
resolution: "finalhandler@npm:1.2.0"
dependencies:
debug: "npm:2.6.9"
encodeurl: "npm:~1.0.2"
escape-html: "npm:~1.0.3"
on-finished: "npm:2.4.1"
parseurl: "npm:~1.3.3"
statuses: "npm:2.0.1"
unpipe: "npm:~1.0.0"
checksum: 64b7e5ff2ad1fcb14931cd012651631b721ce657da24aedb5650ddde9378bf8e95daa451da43398123f5de161a81e79ff5affe4f9f2a6d2df4a813d6d3e254b7
languageName: node
linkType: hard
"find-root@npm:^1.1.0":
version: 1.1.0
resolution: "find-root@npm:1.1.0"
@ -2866,6 +3081,22 @@ __metadata:
languageName: node
linkType: hard
"formdata-polyfill@npm:^4.0.10":
version: 4.0.10
resolution: "formdata-polyfill@npm:4.0.10"
dependencies:
fetch-blob: "npm:^3.1.2"
checksum: 5392ec484f9ce0d5e0d52fb5a78e7486637d516179b0eb84d81389d7eccf9ca2f663079da56f761355c0a65792810e3b345dc24db9a8bbbcf24ef3c8c88570c6
languageName: node
linkType: hard
"forwarded@npm:0.2.0":
version: 0.2.0
resolution: "forwarded@npm:0.2.0"
checksum: 9b67c3fac86acdbc9ae47ba1ddd5f2f81526fa4c8226863ede5600a3f7c7416ef451f6f1e240a3cc32d0fd79fcfe6beb08fd0da454f360032bde70bf80afbb33
languageName: node
linkType: hard
"framer-motion@npm:^4.1.11":
version: 4.1.17
resolution: "framer-motion@npm:4.1.17"
@ -2895,6 +3126,13 @@ __metadata:
languageName: node
linkType: hard
"fresh@npm:0.5.2":
version: 0.5.2
resolution: "fresh@npm:0.5.2"
checksum: c6d27f3ed86cc5b601404822f31c900dd165ba63fff8152a3ef714e2012e7535027063bc67ded4cb5b3a49fa596495d46cacd9f47d6328459cf570f08b7d9e5a
languageName: node
linkType: hard
"fs.realpath@npm:^1.0.0":
version: 1.0.0
resolution: "fs.realpath@npm:1.0.0"
@ -3159,6 +3397,19 @@ __metadata:
languageName: node
linkType: hard
"http-errors@npm:2.0.0":
version: 2.0.0
resolution: "http-errors@npm:2.0.0"
dependencies:
depd: "npm:2.0.0"
inherits: "npm:2.0.4"
setprototypeof: "npm:1.2.0"
statuses: "npm:2.0.1"
toidentifier: "npm:1.0.1"
checksum: fc6f2715fe188d091274b5ffc8b3657bd85c63e969daa68ccb77afb05b071a4b62841acb7a21e417b5539014dff2ebf9550f0b14a9ff126f2734a7c1387f8e19
languageName: node
linkType: hard
"husky@npm:^8.0.3":
version: 8.0.3
resolution: "husky@npm:8.0.3"
@ -3168,6 +3419,15 @@ __metadata:
languageName: node
linkType: hard
"iconv-lite@npm:0.4.24":
version: 0.4.24
resolution: "iconv-lite@npm:0.4.24"
dependencies:
safer-buffer: "npm:>= 2.1.2 < 3"
checksum: c6886a24cc00f2a059767440ec1bc00d334a89f250db8e0f7feb4961c8727118457e27c495ba94d082e51d3baca378726cd110aaf7ded8b9bbfd6a44760cf1d4
languageName: node
linkType: hard
"ignore@npm:^5.2.0, ignore@npm:^5.2.4":
version: 5.2.4
resolution: "ignore@npm:5.2.4"
@ -3215,12 +3475,15 @@ __metadata:
eslint-plugin-jsx-a11y: "npm:^6.8.0"
eslint-plugin-react: "npm:^7.33.2"
eslint-plugin-react-hooks: "npm:^4.6.0"
express: "npm:^4.18.3"
framer-motion: "npm:^4.1.11"
graphql: "npm:^15.5.0"
graphql-tag: "npm:^2.12.6"
husky: "npm:^8.0.3"
immer: "npm:^9.0.6"
lru-cache: "npm:^6.0.0"
node-fetch: "npm:^3.3.2"
oauth2-mock-server: "npm:^7.1.1"
prettier: "npm:^3.0.3"
react: "npm:^18.2.0"
react-autosuggest: "npm:^10.0.2"
@ -3250,7 +3513,7 @@ __metadata:
languageName: node
linkType: hard
"inherits@npm:2, inherits@npm:^2.0.3":
"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.3":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2
@ -3277,6 +3540,13 @@ __metadata:
languageName: node
linkType: hard
"ipaddr.js@npm:1.9.1":
version: 1.9.1
resolution: "ipaddr.js@npm:1.9.1"
checksum: 0486e775047971d3fdb5fb4f063829bac45af299ae0b82dcf3afa2145338e08290563a2a70f34b732d795ecc8311902e541a8530eeb30d75860a78ff4e94ce2a
languageName: node
linkType: hard
"is-array-buffer@npm:^3.0.1, is-array-buffer@npm:^3.0.2":
version: 3.0.2
resolution: "is-array-buffer@npm:3.0.2"
@ -3419,6 +3689,13 @@ __metadata:
languageName: node
linkType: hard
"is-plain-object@npm:^5.0.0":
version: 5.0.0
resolution: "is-plain-object@npm:5.0.0"
checksum: 893e42bad832aae3511c71fd61c0bf61aa3a6d853061c62a307261842727d0d25f761ce9379f7ba7226d6179db2a3157efa918e7fe26360f3bf0842d9f28942c
languageName: node
linkType: hard
"is-regex@npm:^1.1.4":
version: 1.1.4
resolution: "is-regex@npm:1.1.4"
@ -3525,6 +3802,13 @@ __metadata:
languageName: node
linkType: hard
"jose@npm:^4.15.4":
version: 4.15.5
resolution: "jose@npm:4.15.5"
checksum: 9f208492f55ae9c547fd407c36f67ec3385051b5ca390e24f5449740f17359640b3f96fabfd38bc132cc4292b964c31b921bf356253373b1bd3eb6df799b7433
languageName: node
linkType: hard
"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
@ -3668,6 +3952,20 @@ __metadata:
languageName: node
linkType: hard
"media-typer@npm:0.3.0":
version: 0.3.0
resolution: "media-typer@npm:0.3.0"
checksum: d160f31246907e79fed398470285f21bafb45a62869dc469b1c8877f3f064f5eabc4bcc122f9479b8b605bc5c76187d7871cf84c4ee3ecd3e487da1993279928
languageName: node
linkType: hard
"merge-descriptors@npm:1.0.1":
version: 1.0.1
resolution: "merge-descriptors@npm:1.0.1"
checksum: b67d07bd44cfc45cebdec349bb6e1f7b077ee2fd5beb15d1f7af073849208cb6f144fe403e29a36571baf3f4e86469ac39acf13c318381e958e186b2766f54ec
languageName: node
linkType: hard
"merge2@npm:^1.3.0, merge2@npm:^1.4.1":
version: 1.4.1
resolution: "merge2@npm:1.4.1"
@ -3675,6 +3973,13 @@ __metadata:
languageName: node
linkType: hard
"methods@npm:~1.1.2":
version: 1.1.2
resolution: "methods@npm:1.1.2"
checksum: bdf7cc72ff0a33e3eede03708c08983c4d7a173f91348b4b1e4f47d4cdbf734433ad971e7d1e8c77247d9e5cd8adb81ea4c67b0a2db526b758b2233d7814b8b2
languageName: node
linkType: hard
"micromatch@npm:^4.0.4":
version: 4.0.5
resolution: "micromatch@npm:4.0.5"
@ -3685,6 +3990,31 @@ __metadata:
languageName: node
linkType: hard
"mime-db@npm:1.52.0":
version: 1.52.0
resolution: "mime-db@npm:1.52.0"
checksum: 0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa
languageName: node
linkType: hard
"mime-types@npm:~2.1.24, mime-types@npm:~2.1.34":
version: 2.1.35
resolution: "mime-types@npm:2.1.35"
dependencies:
mime-db: "npm:1.52.0"
checksum: 82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2
languageName: node
linkType: hard
"mime@npm:1.6.0":
version: 1.6.0
resolution: "mime@npm:1.6.0"
bin:
mime: cli.js
checksum: b92cd0adc44888c7135a185bfd0dddc42c32606401c72896a842ae15da71eb88858f17669af41e498b463cd7eb998f7b48939a25b08374c7924a9c8a6f8a81b0
languageName: node
linkType: hard
"minimalistic-assert@npm:^1.0.1":
version: 1.0.1
resolution: "minimalistic-assert@npm:1.0.1"
@ -3701,6 +4031,13 @@ __metadata:
languageName: node
linkType: hard
"ms@npm:2.0.0":
version: 2.0.0
resolution: "ms@npm:2.0.0"
checksum: f8fda810b39fd7255bbdc451c46286e549794fcc700dc9cd1d25658bbc4dc2563a5de6fe7c60f798a16a60c6ceb53f033cb353f493f0cf63e5199b702943159d
languageName: node
linkType: hard
"ms@npm:2.1.2":
version: 2.1.2
resolution: "ms@npm:2.1.2"
@ -3708,6 +4045,13 @@ __metadata:
languageName: node
linkType: hard
"ms@npm:2.1.3":
version: 2.1.3
resolution: "ms@npm:2.1.3"
checksum: d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
languageName: node
linkType: hard
"natural-compare@npm:^1.4.0":
version: 1.4.0
resolution: "natural-compare@npm:1.4.0"
@ -3715,6 +4059,46 @@ __metadata:
languageName: node
linkType: hard
"negotiator@npm:0.6.3":
version: 0.6.3
resolution: "negotiator@npm:0.6.3"
checksum: 3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2
languageName: node
linkType: hard
"node-domexception@npm:^1.0.0":
version: 1.0.0
resolution: "node-domexception@npm:1.0.0"
checksum: 5e5d63cda29856402df9472335af4bb13875e1927ad3be861dc5ebde38917aecbf9ae337923777af52a48c426b70148815e890a5d72760f1b4d758cc671b1a2b
languageName: node
linkType: hard
"node-fetch@npm:^3.3.2":
version: 3.3.2
resolution: "node-fetch@npm:3.3.2"
dependencies:
data-uri-to-buffer: "npm:^4.0.0"
fetch-blob: "npm:^3.1.4"
formdata-polyfill: "npm:^4.0.10"
checksum: f3d5e56190562221398c9f5750198b34cf6113aa304e34ee97c94fd300ec578b25b2c2906edba922050fce983338fde0d5d34fcb0fc3336ade5bd0e429ad7538
languageName: node
linkType: hard
"oauth2-mock-server@npm:^7.1.1":
version: 7.1.1
resolution: "oauth2-mock-server@npm:7.1.1"
dependencies:
basic-auth: "npm:^2.0.1"
cors: "npm:^2.8.5"
express: "npm:^4.18.2"
is-plain-object: "npm:^5.0.0"
jose: "npm:^4.15.4"
bin:
oauth2-mock-server: dist/oauth2-mock-server.js
checksum: 79e0871fa272f0f816cf099977825cfd2faa150a8bd82a88819ce84a4ef920fb42cb99f6af7918095b3d399a46d33f5dd13f79e60ff6a979feb38e4f85f89d8c
languageName: node
linkType: hard
"object-assign@npm:^3.0.0":
version: 3.0.0
resolution: "object-assign@npm:3.0.0"
@ -3722,7 +4106,7 @@ __metadata:
languageName: node
linkType: hard
"object-assign@npm:^4.1.1":
"object-assign@npm:^4, object-assign@npm:^4.1.1":
version: 4.1.1
resolution: "object-assign@npm:4.1.1"
checksum: 1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414
@ -3798,6 +4182,15 @@ __metadata:
languageName: node
linkType: hard
"on-finished@npm:2.4.1":
version: 2.4.1
resolution: "on-finished@npm:2.4.1"
dependencies:
ee-first: "npm:1.1.1"
checksum: 46fb11b9063782f2d9968863d9cbba33d77aa13c17f895f56129c274318b86500b22af3a160fe9995aa41317efcd22941b6eba747f718ced08d9a73afdb087b4
languageName: node
linkType: hard
"once@npm:^1.3.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
@ -3871,6 +4264,13 @@ __metadata:
languageName: node
linkType: hard
"parseurl@npm:~1.3.3":
version: 1.3.3
resolution: "parseurl@npm:1.3.3"
checksum: 90dd4760d6f6174adb9f20cf0965ae12e23879b5f5464f38e92fce8073354341e4b3b76fa3d878351efe7d01e617121955284cfd002ab087fba1a0726ec0b4f5
languageName: node
linkType: hard
"path-exists@npm:^4.0.0":
version: 4.0.0
resolution: "path-exists@npm:4.0.0"
@ -3899,6 +4299,13 @@ __metadata:
languageName: node
linkType: hard
"path-to-regexp@npm:0.1.7":
version: 0.1.7
resolution: "path-to-regexp@npm:0.1.7"
checksum: 50a1ddb1af41a9e68bd67ca8e331a705899d16fb720a1ea3a41e310480948387daf603abb14d7b0826c58f10146d49050a1291ba6a82b78a382d1c02c0b8f905
languageName: node
linkType: hard
"path-type@npm:^4.0.0":
version: 4.0.0
resolution: "path-type@npm:4.0.0"
@ -3952,6 +4359,16 @@ __metadata:
languageName: node
linkType: hard
"proxy-addr@npm:~2.0.7":
version: 2.0.7
resolution: "proxy-addr@npm:2.0.7"
dependencies:
forwarded: "npm:0.2.0"
ipaddr.js: "npm:1.9.1"
checksum: c3eed999781a35f7fd935f398b6d8920b6fb00bbc14287bc6de78128ccc1a02c89b95b56742bf7cf0362cc333c61d138532049c7dedc7a328ef13343eff81210
languageName: node
linkType: hard
"punycode@npm:^2.1.0":
version: 2.3.1
resolution: "punycode@npm:2.3.1"
@ -3959,6 +4376,15 @@ __metadata:
languageName: node
linkType: hard
"qs@npm:6.11.0":
version: 6.11.0
resolution: "qs@npm:6.11.0"
dependencies:
side-channel: "npm:^1.0.4"
checksum: 4e4875e4d7c7c31c233d07a448e7e4650f456178b9dd3766b7cfa13158fdb24ecb8c4f059fa91e820dc6ab9f2d243721d071c9c0378892dcdad86e9e9a27c68f
languageName: node
linkType: hard
"queue-microtask@npm:^1.2.2":
version: 1.2.3
resolution: "queue-microtask@npm:1.2.3"
@ -3966,6 +4392,25 @@ __metadata:
languageName: node
linkType: hard
"range-parser@npm:~1.2.1":
version: 1.2.1
resolution: "range-parser@npm:1.2.1"
checksum: 96c032ac2475c8027b7a4e9fe22dc0dfe0f6d90b85e496e0f016fbdb99d6d066de0112e680805075bd989905e2123b3b3d002765149294dce0c1f7f01fcc2ea0
languageName: node
linkType: hard
"raw-body@npm:2.5.2":
version: 2.5.2
resolution: "raw-body@npm:2.5.2"
dependencies:
bytes: "npm:3.1.2"
http-errors: "npm:2.0.0"
iconv-lite: "npm:0.4.24"
unpipe: "npm:1.0.0"
checksum: b201c4b66049369a60e766318caff5cb3cc5a900efd89bdac431463822d976ad0670912c931fdbdcf5543207daf6f6833bca57aa116e1661d2ea91e12ca692c4
languageName: node
linkType: hard
"react-autosuggest@npm:^10.0.2":
version: 10.1.0
resolution: "react-autosuggest@npm:10.1.0"
@ -4289,6 +4734,20 @@ __metadata:
languageName: node
linkType: hard
"safe-buffer@npm:5.1.2":
version: 5.1.2
resolution: "safe-buffer@npm:5.1.2"
checksum: 780ba6b5d99cc9a40f7b951d47152297d0e260f0df01472a1b99d4889679a4b94a13d644f7dbc4f022572f09ae9005fa2fbb93bbbd83643316f365a3e9a45b21
languageName: node
linkType: hard
"safe-buffer@npm:5.2.1":
version: 5.2.1
resolution: "safe-buffer@npm:5.2.1"
checksum: 6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3
languageName: node
linkType: hard
"safe-regex-test@npm:^1.0.0":
version: 1.0.0
resolution: "safe-regex-test@npm:1.0.0"
@ -4300,6 +4759,13 @@ __metadata:
languageName: node
linkType: hard
"safer-buffer@npm:>= 2.1.2 < 3":
version: 2.1.2
resolution: "safer-buffer@npm:2.1.2"
checksum: 7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4
languageName: node
linkType: hard
"scheduler@npm:^0.23.0":
version: 0.23.0
resolution: "scheduler@npm:0.23.0"
@ -4336,6 +4802,39 @@ __metadata:
languageName: node
linkType: hard
"send@npm:0.18.0":
version: 0.18.0
resolution: "send@npm:0.18.0"
dependencies:
debug: "npm:2.6.9"
depd: "npm:2.0.0"
destroy: "npm:1.2.0"
encodeurl: "npm:~1.0.2"
escape-html: "npm:~1.0.3"
etag: "npm:~1.8.1"
fresh: "npm:0.5.2"
http-errors: "npm:2.0.0"
mime: "npm:1.6.0"
ms: "npm:2.1.3"
on-finished: "npm:2.4.1"
range-parser: "npm:~1.2.1"
statuses: "npm:2.0.1"
checksum: 0eb134d6a51fc13bbcb976a1f4214ea1e33f242fae046efc311e80aff66c7a43603e26a79d9d06670283a13000e51be6e0a2cb80ff0942eaf9f1cd30b7ae736a
languageName: node
linkType: hard
"serve-static@npm:1.15.0":
version: 1.15.0
resolution: "serve-static@npm:1.15.0"
dependencies:
encodeurl: "npm:~1.0.2"
escape-html: "npm:~1.0.3"
parseurl: "npm:~1.3.3"
send: "npm:0.18.0"
checksum: fa9f0e21a540a28f301258dfe1e57bb4f81cd460d28f0e973860477dd4acef946a1f41748b5bd41c73b621bea2029569c935faa38578fd34cd42a9b4947088ba
languageName: node
linkType: hard
"set-function-length@npm:^1.1.1":
version: 1.1.1
resolution: "set-function-length@npm:1.1.1"
@ -4359,6 +4858,13 @@ __metadata:
languageName: node
linkType: hard
"setprototypeof@npm:1.2.0":
version: 1.2.0
resolution: "setprototypeof@npm:1.2.0"
checksum: 68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc
languageName: node
linkType: hard
"shallow-equal@npm:^1.2.1":
version: 1.2.1
resolution: "shallow-equal@npm:1.2.1"
@ -4407,6 +4913,13 @@ __metadata:
languageName: node
linkType: hard
"statuses@npm:2.0.1":
version: 2.0.1
resolution: "statuses@npm:2.0.1"
checksum: 34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0
languageName: node
linkType: hard
"string.prototype.matchall@npm:^4.0.8":
version: 4.0.10
resolution: "string.prototype.matchall@npm:4.0.10"
@ -4559,6 +5072,13 @@ __metadata:
languageName: node
linkType: hard
"toidentifier@npm:1.0.1":
version: 1.0.1
resolution: "toidentifier@npm:1.0.1"
checksum: 93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1
languageName: node
linkType: hard
"ts-api-utils@npm:^1.0.1":
version: 1.0.3
resolution: "ts-api-utils@npm:1.0.3"
@ -4623,6 +5143,16 @@ __metadata:
languageName: node
linkType: hard
"type-is@npm:~1.6.18":
version: 1.6.18
resolution: "type-is@npm:1.6.18"
dependencies:
media-typer: "npm:0.3.0"
mime-types: "npm:~2.1.24"
checksum: a23daeb538591b7efbd61ecf06b6feb2501b683ffdc9a19c74ef5baba362b4347e42f1b4ed81f5882a8c96a3bfff7f93ce3ffaf0cbbc879b532b04c97a55db9d
languageName: node
linkType: hard
"typed-array-buffer@npm:^1.0.0":
version: 1.0.0
resolution: "typed-array-buffer@npm:1.0.0"
@ -4702,6 +5232,13 @@ __metadata:
languageName: node
linkType: hard
"unpipe@npm:1.0.0, unpipe@npm:~1.0.0":
version: 1.0.0
resolution: "unpipe@npm:1.0.0"
checksum: 193400255bd48968e5c5383730344fbb4fa114cdedfab26e329e50dd2d81b134244bb8a72c6ac1b10ab0281a58b363d06405632c9d49ca9dfd5e90cbd7d0f32c
languageName: node
linkType: hard
"uri-js@npm:^4.2.2":
version: 4.4.1
resolution: "uri-js@npm:4.4.1"
@ -4742,6 +5279,20 @@ __metadata:
languageName: node
linkType: hard
"utils-merge@npm:1.0.1":
version: 1.0.1
resolution: "utils-merge@npm:1.0.1"
checksum: 02ba649de1b7ca8854bfe20a82f1dfbdda3fb57a22ab4a8972a63a34553cf7aa51bc9081cf7e001b035b88186d23689d69e71b510e610a09a4c66f68aa95b672
languageName: node
linkType: hard
"vary@npm:^1, vary@npm:~1.1.2":
version: 1.1.2
resolution: "vary@npm:1.1.2"
checksum: f15d588d79f3675135ba783c91a4083dcd290a2a5be9fcb6514220a1634e23df116847b1cc51f66bfb0644cf9353b2abb7815ae499bab06e46dd33c1a6bf1f4f
languageName: node
linkType: hard
"warning@npm:^4.0.3":
version: 4.0.3
resolution: "warning@npm:4.0.3"
@ -4751,6 +5302,13 @@ __metadata:
languageName: node
linkType: hard
"web-streams-polyfill@npm:^3.0.3":
version: 3.3.3
resolution: "web-streams-polyfill@npm:3.3.3"
checksum: 64e855c47f6c8330b5436147db1c75cb7e7474d924166800e8e2aab5eb6c76aac4981a84261dd2982b3e754490900b99791c80ae1407a9fa0dcff74f82ea3a7f
languageName: node
linkType: hard
"which-boxed-primitive@npm:^1.0.2":
version: 1.0.2
resolution: "which-boxed-primitive@npm:1.0.2"