Skip to content

Commit 4d6a3dc

Browse files
jjmatasokie
authored andcommitted
Add OpenID Connect login support (we-promise#77)
* Add OpenID Connect login support * Add docs for OIDC config with Google Auth * Use Google styles for log in - Add support for linking existing account - Force users to sign-in with passoword first, when linking existing accounts - Add support to create new user when using OIDC - Add identities to user to prevent account take-ver - Make tests mocking instead of being integration tests - Manage session handling correctly - use OmniAuth.config.mock_auth instead of passing auth data via request env * Conditionally render Oauth button - Set a config item `configuration.x.auth.oidc_enabled` - Hide button if disabled --------- Signed-off-by: Juan José Mata <[email protected]> Signed-off-by: soky srm <[email protected]> Co-authored-by: sokie <[email protected]>
1 parent 3cf15de commit 4d6a3dc

29 files changed

+997
-25
lines changed

.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ POSTGRES_USER=postgres
6262
# This is the domain that your Sure instance will be hosted at. It is used to generate links in emails and other places.
6363
APP_DOMAIN=
6464

65+
# OpenID Connect configuration
66+
OIDC_CLIENT_ID=
67+
OIDC_CLIENT_SECRET=
68+
OIDC_ISSUER=
69+
OIDC_REDIRECT_URI=
70+
6571
# Product/Brand Name
6672
PRODUCT_NAME=
6773
BRAND_NAME=

.env.local.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ OPENAI_MODEL =
1313
# OPENAI_URI_BASE = http://host.docker.internal:1234/
1414
# OPENAI_MODEL = qwen/qwen3-vl-4b
1515

16+
# OpenID Connect for development
17+
OIDC_CLIENT_ID=
18+
OIDC_CLIENT_SECRET=
19+
OIDC_ISSUER=
20+
OIDC_REDIRECT_URI=http://localhost:3000/auth/openid_connect/callback
21+
1622
# Langfuse config
1723
LANGFUSE_PUBLIC_KEY =
1824
LANGFUSE_SECRET_KEY =

.env.test.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
SELF_HOSTED=false
22

3+
# OpenID Connect for tests
4+
OIDC_ISSUER=
5+
OIDC_CLIENT_ID=
6+
OIDC_CLIENT_SECRET=
7+
OIDC_REDIRECT_URI=http://localhost:3000/auth/openid_connect/callback
8+
39
# ================
410
# Data Providers
511
# ---------------------------------------------------------------------------------

Gemfile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ gem "rqrcode", "~> 3.0"
7575
gem "activerecord-import"
7676
gem "rubyzip", "~> 2.3"
7777

78+
# OpenID Connect authentication
79+
gem "omniauth", "~> 2.1"
80+
gem "omniauth-rails_csrf_protection"
81+
gem "omniauth_openid_connect"
82+
7883
# State machines
7984
gem "aasm"
8085
gem "after_commit_everywhere", "~> 1.0"

Gemfile.lock

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,12 @@ GEM
8585
tzinfo (~> 2.0, >= 2.0.5)
8686
addressable (2.8.7)
8787
public_suffix (>= 2.0.2, < 7.0)
88+
aes_key_wrap (1.1.0)
8889
after_commit_everywhere (1.6.0)
8990
activerecord (>= 4.2)
9091
activesupport
9192
ast (2.4.3)
93+
attr_required (1.0.2)
9294
aws-eventstream (1.4.0)
9395
aws-partitions (1.1113.0)
9496
aws-sdk-core (3.225.1)
@@ -119,6 +121,7 @@ GEM
119121
parser (>= 2.4)
120122
smart_properties
121123
bigdecimal (3.2.2)
124+
bindata (2.5.1)
122125
bindex (0.8.1)
123126
bootsnap (1.18.6)
124127
msgpack (~> 1.2)
@@ -182,6 +185,8 @@ GEM
182185
dotenv (= 3.1.8)
183186
railties (>= 6.1)
184187
drb (2.2.3)
188+
email_validator (2.2.4)
189+
activemodel
185190
erb (5.0.1)
186191
erb_lint (0.9.0)
187192
activesupport
@@ -200,6 +205,8 @@ GEM
200205
faraday-net_http (>= 2.0, < 3.5)
201206
json
202207
logger
208+
faraday-follow_redirects (0.3.0)
209+
faraday (>= 1, < 3)
203210
faraday-multipart (1.1.1)
204211
multipart-post (~> 2.0)
205212
faraday-net_http (3.4.1)
@@ -224,6 +231,7 @@ GEM
224231
globalid (1.2.1)
225232
activesupport (>= 6.1)
226233
hashdiff (1.2.0)
234+
hashie (5.0.0)
227235
heapy (0.2.0)
228236
thor
229237
highline (3.1.2)
@@ -276,6 +284,13 @@ GEM
276284
activesupport (>= 5.0.0)
277285
jmespath (1.6.2)
278286
json (2.12.2)
287+
json-jwt (1.16.7)
288+
activesupport (>= 4.2)
289+
aes_key_wrap
290+
base64
291+
bindata
292+
faraday (~> 2.0)
293+
faraday-follow_redirects
279294
jwt (2.10.2)
280295
base64
281296
langfuse-ruby (0.1.4)
@@ -374,6 +389,29 @@ GEM
374389
octokit (10.0.0)
375390
faraday (>= 1, < 3)
376391
sawyer (~> 0.9)
392+
omniauth (2.1.3)
393+
hashie (>= 3.4.6)
394+
rack (>= 2.2.3)
395+
rack-protection
396+
omniauth-rails_csrf_protection (1.0.2)
397+
actionpack (>= 4.2)
398+
omniauth (~> 2.0)
399+
omniauth_openid_connect (0.8.0)
400+
omniauth (>= 1.9, < 3)
401+
openid_connect (~> 2.2)
402+
openid_connect (2.3.1)
403+
activemodel
404+
attr_required (>= 1.0.0)
405+
email_validator
406+
faraday (~> 2.0)
407+
faraday-follow_redirects
408+
json-jwt (>= 1.16)
409+
mail
410+
rack-oauth2 (~> 2.2)
411+
swd (~> 2.0)
412+
tzinfo
413+
validate_url
414+
webfinger (~> 2.0)
377415
ostruct (0.6.2)
378416
pagy (9.3.5)
379417
parallel (1.27.0)
@@ -409,6 +447,17 @@ GEM
409447
rack (>= 1.0, < 4)
410448
rack-mini-profiler (4.0.0)
411449
rack (>= 1.2.0)
450+
rack-oauth2 (2.2.1)
451+
activesupport
452+
attr_required
453+
faraday (~> 2.0)
454+
faraday-follow_redirects
455+
json-jwt (>= 1.11.0)
456+
rack (>= 2.1.0)
457+
rack-protection (4.1.1)
458+
base64 (>= 0.1.0)
459+
logger (>= 1.6.0)
460+
rack (>= 3.0.0, < 4)
412461
rack-session (2.1.1)
413462
base64 (>= 0.1.0)
414463
rack (>= 3.0.0)
@@ -567,6 +616,11 @@ GEM
567616
railties (>= 6.0.0)
568617
stringio (3.1.7)
569618
stripe (15.3.0)
619+
swd (2.0.3)
620+
activesupport (>= 3)
621+
attr_required (>= 0.0.5)
622+
faraday (~> 2.0)
623+
faraday-follow_redirects
570624
tailwindcss-rails (4.2.3)
571625
railties (>= 7.0.0)
572626
tailwindcss-ruby (~> 4.0)
@@ -593,6 +647,9 @@ GEM
593647
unicode-emoji (4.0.4)
594648
uri (1.0.3)
595649
useragent (0.16.11)
650+
validate_url (1.0.15)
651+
activemodel (>= 3.0.0)
652+
public_suffix
596653
vcr (6.3.1)
597654
base64
598655
vernier (1.8.0)
@@ -605,6 +662,10 @@ GEM
605662
activemodel (>= 6.0.0)
606663
bindex (>= 0.4.0)
607664
railties (>= 6.0.0)
665+
webfinger (2.1.3)
666+
activesupport
667+
faraday (~> 2.0)
668+
faraday-follow_redirects
608669
webmock (3.25.1)
609670
addressable (>= 2.8.0)
610671
crack (>= 0.3.2)
@@ -668,6 +729,9 @@ DEPENDENCIES
668729
lucide-rails!
669730
mocha
670731
octokit
732+
omniauth (~> 2.1)
733+
omniauth-rails_csrf_protection
734+
omniauth_openid_connect
671735
ostruct
672736
pagy
673737
pg (~> 1.5)

app/assets/tailwind/application.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
@import "./simonweb_pickr.css";
1212

13+
@import "./google-sign-in.css";
14+
1315
@layer components {
1416
.pcr-app{
1517
position: static !important;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
@layer components {
2+
.gsi-material-button {
3+
-moz-user-select: none;
4+
-webkit-user-select: none;
5+
-ms-user-select: none;
6+
-webkit-appearance: none;
7+
background-color: WHITE;
8+
background-image: none;
9+
border: 1px solid #747775;
10+
-webkit-border-radius: 4px;
11+
border-radius: 4px;
12+
-webkit-box-sizing: border-box;
13+
box-sizing: border-box;
14+
color: #1f1f1f;
15+
cursor: pointer;
16+
font-family: 'Roboto', arial, sans-serif;
17+
font-size: 14px;
18+
height: 40px;
19+
letter-spacing: 0.25px;
20+
outline: none;
21+
overflow: hidden;
22+
padding: 0 12px;
23+
position: relative;
24+
text-align: center;
25+
-webkit-transition: background-color .218s, border-color .218s, box-shadow .218s;
26+
transition: background-color .218s, border-color .218s, box-shadow .218s;
27+
vertical-align: middle;
28+
white-space: nowrap;
29+
width: auto;
30+
max-width: 400px;
31+
min-width: min-content;
32+
display: inline-flex;
33+
}
34+
35+
.gsi-material-button .gsi-material-button-icon {
36+
height: 20px;
37+
margin-right: 12px;
38+
min-width: 20px;
39+
width: 20px;
40+
}
41+
42+
.gsi-material-button .gsi-material-button-content-wrapper {
43+
-webkit-align-items: center;
44+
align-items: center;
45+
display: flex;
46+
-webkit-flex-direction: row;
47+
flex-direction: row;
48+
-webkit-flex-wrap: nowrap;
49+
flex-wrap: nowrap;
50+
height: 100%;
51+
justify-content: space-between;
52+
position: relative;
53+
width: 100%;
54+
}
55+
56+
.gsi-material-button .gsi-material-button-contents {
57+
-webkit-flex-grow: 1;
58+
flex-grow: 1;
59+
font-family: 'Roboto', arial, sans-serif;
60+
font-weight: 500;
61+
overflow: hidden;
62+
text-overflow: ellipsis;
63+
vertical-align: top;
64+
}
65+
66+
.gsi-material-button .gsi-material-button-state {
67+
-webkit-transition: opacity .218s;
68+
transition: opacity .218s;
69+
bottom: 0;
70+
left: 0;
71+
opacity: 0;
72+
position: absolute;
73+
right: 0;
74+
top: 0;
75+
}
76+
77+
.gsi-material-button:disabled {
78+
cursor: default;
79+
background-color: #ffffff61;
80+
border-color: #1f1f1f1f;
81+
}
82+
83+
.gsi-material-button:disabled .gsi-material-button-contents {
84+
opacity: 38%;
85+
}
86+
87+
.gsi-material-button:disabled .gsi-material-button-icon {
88+
opacity: 38%;
89+
}
90+
91+
.gsi-material-button:not(:disabled):active .gsi-material-button-state,
92+
.gsi-material-button:not(:disabled):focus .gsi-material-button-state {
93+
background-color: #303030;
94+
opacity: 12%;
95+
}
96+
97+
.gsi-material-button:not(:disabled):hover {
98+
-webkit-box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .30), 0 1px 3px 1px rgba(60, 64, 67, .15);
99+
box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .30), 0 1px 3px 1px rgba(60, 64, 67, .15);
100+
}
101+
102+
.gsi-material-button:not(:disabled):hover .gsi-material-button-state {
103+
background-color: #303030;
104+
opacity: 8%;
105+
}
106+
}

0 commit comments

Comments
 (0)