Skip to content

Commit 177ee2f

Browse files
tamassolteszsattvikcporcellus
authored
feat: webauthn base (#1115)
* feat: new dependency: webauthn4j * feat: add tables for webauthn * fix: typo fixes * feat: webauthn options * feat: registercredentials wip * feat: passkeys register credentials wip * feat: recipe user sign up * recipe user creation wip * sign up recipe user * feat: register credentials * fix: temp * feat: webauthn support wip * feat: webauthn support wip * merging * feat: webauthn support wip * feat: getuserinfolist draft * feat: get user by account info - webauthn support * fix: generate account recovery token api * feat: get user by account info - webauthn support * feat: signup with credentialsregister * fix: fixes for tests * fix: fixes for tests * feat: get generated options api * feat: webauthn sign in * fix: account recovery * fix: fixes for tests * fix: fixing id name in response * fix: fixing id encoding in response * fix: base64 url encode the challenge insted of base64 encode * fix: account recovery impl * fix: base64 encoding changes * fix: fixes for tests * fix: fixing sql issues and encoding issues * fix: fixes for tests * fix: integration fix for signup * fix: webauthn flow test stub * fix: fixes for sdk tests * feat: add webauthn recover account apis to webserver * feat: crud apis addition * feat: remove options api * fix: additional field in the sign in options response * fix: reworked error handling * fix: fixing GET api not to expect json body * fix: sign in + options check * fix: more descriptive error messages for credentialsRegister * fix: typo fixes * fix: fixes for tests * fix: not letting dependencies exception to leak out * feat: clean up expired data cron * fix: changing recovery token consume * fix: fixing loginmethod collection * fix: signin fixes * fix: webauthn sign in fixes * fix: add recipeUserId in signIn response * feat: enable credentials listing api * feat: extending user listing with webauthn * fix: don't use the counter at signin check * fix: small fixes * fix: setting UV and RK to false * feat: saving userVerification and userPresence values * fix: change a bunch of error messages for sdk integration * fix: change a bunch of error messages for sdk integration * fix: include userVerification and userPresence in options response * fix: error messages changes * fix: refactor exceptions * feat: get credential api * fix: options generation no longer throws invalid options error as per reference impl * fix: more error handling for sign in * fix: rename methods for better readability * fix: throw the right exception * ci: experiment with a GHA to publish test/dev images * ci: experiment with publishing dev docker images * ci: experiment with publishing dev docker images * ci: experiment with a GHA to publish test/dev images * fix: options validation * fix: options validation rpId doesn't have to be an url * fix: additional validation * fix: fixes for various sdk tests * fix: Dockerfile setupTestEnv --local * ci: remove arm64 build from dev-docker * fix: add webauth4jn-test dependency * fix: fixing email verification query for webauthn * fix: authenticator mocking and example usage * fix: sem ver and few test fixes * fix: test fixes * fix: cdi version increment in webauthn test * fix: webauthn signIn should load all loginmethods of the user * fix: fixing table locked issue with in memory db * fix: remove unnecessary logging * fix: add tests and fixes * fix: add tests and fixes for email update * fix: additional tests and fixes related to useridmapping * fix: add null check * fix: add test * fix: additional indexes for performance optimization * ci: fix dev-docker build * fix: self-review fixes * fix: update pluginInterfaceSupported to the right branch * chore: changelog, version number * fix: review fixes * fix: review fixes * fix: fixing email verified flag after email change * fix: review fixes * fix: handling potential error while saving options * fix: review fixes * chore: updating supported pluginInterface * chore: updating supported pluginInterface * test: API tests (#1118) * fix: API tests template * fix: options register APIs * test: register credential * test: fix * fix: test get credential * test: list credential * test: remove credential * test: remove credential * test: sign in options * test: sign-in * test: sign-in * test: update email * fix: delete * fix: tests for mongodb * fix: tests * fix: tests * fix: review fixes * fix: review fix: token generation changes --------- Co-authored-by: Sattvik Chakravarthy <[email protected]> Co-authored-by: Mihaly Lengyel <[email protected]> Co-authored-by: Sattvik Chakravarthy <[email protected]>
1 parent b947894 commit 177ee2f

File tree

88 files changed

+9349
-152
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+9349
-152
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,7 @@ local.properties
4747
*.iml
4848
ee/bin
4949
addDevTag
50-
addReleaseTag
50+
addReleaseTag
51+
52+
install-linux.sh
53+
install-windows.bat

CHANGELOG.md

+185
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,191 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
99

10+
## [10.1.0]
11+
12+
- Adds Webauthn (Passkeys) support to core
13+
- Adds APIs:
14+
- GET `/recipe/webauthn/user/credential/`
15+
- GET `/recipe/webauthn/user/credential/list`
16+
- GET `/recipe/webauthn/options`
17+
- GET `/recipe/webauthn/user/recover`
18+
- POST `/recipe/webauthn/options/register`
19+
- POST `/recipe/webauthn/options/signin`
20+
- POST `/recipe/webauthn/user/credential/register`
21+
- POST `/recipe/webauthn/signup`
22+
- POST `/recipe/webauthn/signin`
23+
- POST `/recipe/webauthn/user/recover/token`
24+
- POST `/recipe/webauthn/user/recover/token/consume`
25+
- PUT `/recipe/webauthn/user/email`
26+
- DELETE `/recipe/webauthn/user/credential/remove`
27+
- DELETE `/recipe/webauthn/options/remove`
28+
- Adds additional indexing for `emailverification_verified_emails`
29+
30+
### Migration
31+
32+
If using PostgreSQL, run the following SQL script:
33+
34+
```sql
35+
36+
CREATE INDEX IF NOT EXISTS emailverification_verified_emails_app_id_email_index ON emailverification_verified_emails
37+
(app_id, email);
38+
39+
CREATE TABLE IF NOT EXISTS webauthn_account_recovery_tokens (
40+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
41+
tenant_id VARCHAR(64) DEFAULT 'public' NOT NULL,
42+
user_id CHAR(36) NOT NULL,
43+
email VARCHAR(256) NOT NULL,
44+
token VARCHAR(256) NOT NULL,
45+
expires_at BIGINT NOT NULL,
46+
CONSTRAINT webauthn_account_recovery_token_pkey PRIMARY KEY (app_id, tenant_id, user_id, token),
47+
CONSTRAINT webauthn_account_recovery_token_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES
48+
all_auth_recipe_users(app_id, tenant_id, user_id) ON DELETE CASCADE
49+
);
50+
51+
CREATE TABLE IF NOT EXISTS webauthn_credentials (
52+
id VARCHAR(256) NOT NULL,
53+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
54+
rp_id VARCHAR(256) NOT NULL,
55+
user_id CHAR(36),
56+
counter BIGINT NOT NULL,
57+
public_key BYTEA NOT NULL,
58+
transports TEXT NOT NULL,
59+
created_at BIGINT NOT NULL,
60+
updated_at BIGINT NOT NULL,
61+
CONSTRAINT webauthn_credentials_pkey PRIMARY KEY (app_id, rp_id, id),
62+
CONSTRAINT webauthn_credentials_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES webauthn_users
63+
(app_id, user_id) ON DELETE CASCADE
64+
);
65+
66+
CREATE TABLE IF NOT EXISTS webauthn_generated_options (
67+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
68+
tenant_id VARCHAR(64) DEFAULT 'public'NOT NULL,
69+
id CHAR(36) NOT NULL,
70+
challenge VARCHAR(256) NOT NULL,
71+
email VARCHAR(256),
72+
rp_id VARCHAR(256) NOT NULL,
73+
rp_name VARCHAR(256) NOT NULL,
74+
origin VARCHAR(256) NOT NULL,
75+
expires_at BIGINT NOT NULL,
76+
created_at BIGINT NOT NULL,
77+
user_presence_required BOOLEAN DEFAULT false NOT NULL,
78+
user_verification VARCHAR(12) DEFAULT 'preferred' NOT NULL,
79+
CONSTRAINT webauthn_generated_options_pkey PRIMARY KEY (app_id, tenant_id, id),
80+
CONSTRAINT webauthn_generated_options_tenant_id_fkey FOREIGN KEY (app_id, tenant_id) REFERENCES tenants
81+
(app_id, tenant_id) ON DELETE CASCADE
82+
);
83+
84+
CREATE TABLE IF NOT EXISTS webauthn_user_to_tenant (
85+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
86+
tenant_id VARCHAR(64) DEFAULT 'public' NOT NULL,
87+
user_id CHAR(36) NOT NULL,
88+
email VARCHAR(256) NOT NULL,
89+
CONSTRAINT webauthn_user_to_tenant_email_key UNIQUE (app_id, tenant_id, email),
90+
CONSTRAINT webauthn_user_to_tenant_pkey PRIMARY KEY (app_id, tenant_id, user_id),
91+
CONSTRAINT webauthn_user_to_tenant_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES
92+
all_auth_recipe_users(app_id, tenant_id, user_id) ON DELETE CASCADE
93+
);
94+
95+
CREATE TABLE IF NOT EXISTS webauthn_users (
96+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
97+
user_id CHAR(36) NOT NULL,
98+
email VARCHAR(256) NOT NULL,
99+
rp_id VARCHAR(256) NOT NULL,
100+
time_joined BIGINT NOT NULL,
101+
CONSTRAINT webauthn_users_pkey PRIMARY KEY (app_id, user_id),
102+
CONSTRAINT webauthn_users_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id(app_id,
103+
user_id) ON DELETE CASCADE
104+
);
105+
106+
CREATE INDEX IF NOT EXISTS webauthn_user_to_tenant_email_index ON webauthn_user_to_tenant (app_id, email);
107+
CREATE INDEX IF NOT EXISTS webauthn_user_challenges_expires_at_index ON webauthn_generated_options (app_id, tenant_id, expires_at);
108+
CREATE INDEX IF NOT EXISTS webauthn_credentials_user_id_index ON webauthn_credentials (user_id);
109+
CREATE INDEX IF NOT EXISTS webauthn_account_recovery_token_token_index ON webauthn_account_recovery_tokens (app_id, tenant_id, token);
110+
CREATE INDEX IF NOT EXISTS webauthn_account_recovery_token_expires_at_index ON webauthn_account_recovery_tokens (expires_at DESC);
111+
CREATE INDEX IF NOT EXISTS webauthn_account_recovery_token_email_index ON webauthn_account_recovery_tokens (app_id, tenant_id, email);
112+
```
113+
114+
If using MySQL, run the following SQL script:
115+
116+
```sql
117+
CREATE INDEX emailverification_verified_emails_app_id_email_index ON emailverification_verified_emails
118+
(app_id, email);
119+
120+
CREATE TABLE IF NOT EXISTS webauthn_account_recovery_tokens (
121+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
122+
tenant_id VARCHAR(64) DEFAULT 'public' NOT NULL,
123+
user_id CHAR(36) NOT NULL,
124+
email VARCHAR(256) NOT NULL,
125+
token VARCHAR(256) NOT NULL,
126+
expires_at BIGINT NOT NULL,
127+
CONSTRAINT webauthn_account_recovery_token_pkey PRIMARY KEY (app_id, tenant_id, user_id, token),
128+
CONSTRAINT webauthn_account_recovery_token_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES
129+
all_auth_recipe_users(app_id, tenant_id, user_id) ON DELETE CASCADE
130+
);
131+
132+
CREATE TABLE IF NOT EXISTS webauthn_credentials (
133+
id VARCHAR(256) NOT NULL,
134+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
135+
rp_id VARCHAR(256) NOT NULL,
136+
user_id CHAR(36),
137+
counter BIGINT NOT NULL,
138+
public_key BLOB NOT NULL,
139+
transports TEXT NOT NULL,
140+
created_at BIGINT NOT NULL,
141+
updated_at BIGINT NOT NULL,
142+
CONSTRAINT webauthn_credentials_pkey PRIMARY KEY (app_id, rp_id, id),
143+
CONSTRAINT webauthn_credentials_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES webauthn_users
144+
(app_id, user_id) ON DELETE CASCADE
145+
);
146+
147+
CREATE TABLE IF NOT EXISTS webauthn_generated_options (
148+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
149+
tenant_id VARCHAR(64) DEFAULT 'public'NOT NULL,
150+
id CHAR(36) NOT NULL,
151+
challenge VARCHAR(256) NOT NULL,
152+
email VARCHAR(256),
153+
rp_id VARCHAR(256) NOT NULL,
154+
rp_name VARCHAR(256) NOT NULL,
155+
origin VARCHAR(256) NOT NULL,
156+
expires_at BIGINT NOT NULL,
157+
created_at BIGINT NOT NULL,
158+
user_presence_required BOOLEAN DEFAULT false NOT NULL,
159+
user_verification VARCHAR(12) DEFAULT 'preferred' NOT NULL,
160+
CONSTRAINT webauthn_generated_options_pkey PRIMARY KEY (app_id, tenant_id, id),
161+
CONSTRAINT webauthn_generated_options_tenant_id_fkey FOREIGN KEY (app_id, tenant_id) REFERENCES tenants
162+
(app_id, tenant_id) ON DELETE CASCADE
163+
);
164+
165+
CREATE TABLE IF NOT EXISTS webauthn_user_to_tenant (
166+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
167+
tenant_id VARCHAR(64) DEFAULT 'public' NOT NULL,
168+
user_id CHAR(36) NOT NULL,
169+
email VARCHAR(256) NOT NULL,
170+
CONSTRAINT webauthn_user_to_tenant_email_key UNIQUE (app_id, tenant_id, email),
171+
CONSTRAINT webauthn_user_to_tenant_pkey PRIMARY KEY (app_id, tenant_id, user_id),
172+
CONSTRAINT webauthn_user_to_tenant_user_id_fkey FOREIGN KEY (app_id, tenant_id, user_id) REFERENCES
173+
all_auth_recipe_users(app_id, tenant_id, user_id) ON DELETE CASCADE
174+
);
175+
176+
CREATE TABLE IF NOT EXISTS webauthn_users (
177+
app_id VARCHAR(64) DEFAULT 'public' NOT NULL,
178+
user_id CHAR(36) NOT NULL,
179+
email VARCHAR(256) NOT NULL,
180+
rp_id VARCHAR(256) NOT NULL,
181+
time_joined BIGINT NOT NULL,
182+
CONSTRAINT webauthn_users_pkey PRIMARY KEY (app_id, user_id),
183+
CONSTRAINT webauthn_users_user_id_fkey FOREIGN KEY (app_id, user_id) REFERENCES app_id_to_user_id (app_id,
184+
user_id) ON DELETE CASCADE
185+
);
186+
187+
CREATE INDEX webauthn_user_to_tenant_email_index ON webauthn_user_to_tenant (app_id, email);
188+
CREATE INDEX webauthn_user_challenges_expires_at_index ON webauthn_generated_options (app_id, tenant_id, expires_at);
189+
CREATE INDEX webauthn_credentials_user_id_index ON webauthn_credentials (user_id);
190+
CREATE INDEX webauthn_account_recovery_token_token_index ON webauthn_account_recovery_tokens (app_id, tenant_id, token);
191+
CREATE INDEX webauthn_account_recovery_token_expires_at_index ON webauthn_account_recovery_tokens (expires_at DESC);
192+
CREATE INDEX webauthn_account_recovery_token_email_index ON webauthn_account_recovery_tokens (app_id, tenant_id, email);
193+
```
194+
10195
## [10.0.3]
11196

12197
- Fixes `StorageTransactionLogicException` in bulk import when not using userRoles and totpDevices in import json.

build.gradle

+7-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" }
1919
// }
2020
//}
2121

22-
version = "10.0.3"
22+
version = "10.1.0"
2323

2424
repositories {
2525
mavenCentral()
@@ -73,6 +73,12 @@ dependencies {
7373
// https://mvnrepository.com/artifact/com.googlecode.libphonenumber/libphonenumber/
7474
implementation group: 'com.googlecode.libphonenumber', name: 'libphonenumber', version: '8.13.25'
7575

76+
// https://mvnrepository.com/artifact/com.webauthn4j/webauthn4j-core
77+
implementation group: 'com.webauthn4j', name: 'webauthn4j-core', version: '0.28.5.RELEASE'
78+
79+
// https://mvnrepository.com/artifact/com.webauthn4j/webauthn4j-test
80+
implementation group: 'com.webauthn4j', name: 'webauthn4j-test', version: '0.28.5.RELEASE'
81+
7682
compileOnly project(":supertokens-plugin-interface")
7783
testImplementation project(":supertokens-plugin-interface")
7884

@@ -86,7 +92,6 @@ dependencies {
8692
testImplementation group: 'org.reflections', name: 'reflections', version: '0.9.10'
8793

8894
testImplementation 'com.tngtech.archunit:archunit-junit4:0.22.0'
89-
9095
}
9196

9297
application {

config.yaml

+4
Original file line numberDiff line numberDiff line change
@@ -174,3 +174,7 @@ core_config_version: 0
174174
# (DIFFERENT_ACROSS_APPS | OPTIONAL | Default: number of available processor cores) int value. If specified,
175175
# the supertokens core will use the specified number of threads to complete the migration of users.
176176
# bulk_migration_parallelism:
177+
178+
# (DIFFERENT_ACROSS_APPS | OPTIONAL | Default: 3600000) long value. Time in milliseconds for how long a webauthn
179+
# account recovery token is valid for.
180+
# webauthn_recover_account_token_lifetime:

coreDriverInterfaceSupported.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"4.0",
2222
"5.0",
2323
"5.1",
24-
"5.2"
24+
"5.2",
25+
"5.3"
2526
]
2627
}

devConfig.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,6 @@ disable_telemetry: true
175175
# the supertokens core will use the specified number of threads to complete the migration of users.
176176
# bulk_migration_parallelism:
177177

178+
# (DIFFERENT_ACROSS_APPS | OPTIONAL | Default: 3600000) long value. Time in milliseconds for how long a webauthn
179+
# account recovery token is valid for.
180+
# webauthn_recover_account_token_lifetime:

implementationDependencies.json

+10
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@
115115
"jar": "https://repo1.maven.org/maven2/com/googlecode/libphonenumber/libphonenumber/8.13.25/libphonenumber-8.13.25.jar",
116116
"name": "Libphonenumber 8.13.25",
117117
"src": "https://repo1.maven.org/maven2/com/googlecode/libphonenumber/libphonenumber/8.13.25/libphonenumber-8.13.25-sources.jar"
118+
},
119+
{
120+
"jar": "https://repo1.maven.org/maven2/com/webauthn4j/webauthn4j-core/0.28.3.RELEASE/webauthn4j-core-0.28.5.RELEASE.jar",
121+
"name": "webauthn4j-core 0.28.3.RELEASE",
122+
"src": "https://repo1.maven.org/maven2/com/webauthn4j/webauthn4j-core/0.28.3.RELEASE/webauthn4j-core-0.28.5.RELEASE-sources.jar"
123+
},
124+
{
125+
"jar": "https://repo1.maven.org/maven2/com/webauthn4j/webauthn4j-test/0.28.5.RELEASE/webauthn4j-test-0.28.5.RELEASE.jar",
126+
"name": "webauthn4j-test 0.28.5.RELEASE",
127+
"src": "https://repo1.maven.org/maven2/com/webauthn4j/webauthn4j-test/0.28.5.RELEASE/webauthn4j-test-0.28.5.RELEASE-sources.jar"
118128
}
119129
]
120130
}

pluginInterfaceSupported.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"_comment": "contains a list of plugin interfaces branch names that this core supports",
33
"versions": [
4-
"7.0"
4+
"7.1"
55
]
66
}

src/main/java/io/supertokens/Main.java

+3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.supertokens.cronjobs.Cronjobs;
2323
import io.supertokens.cronjobs.bulkimport.ProcessBulkImportUsers;
2424
import io.supertokens.cronjobs.cleanupOAuthSessionsAndChallenges.CleanupOAuthSessionsAndChallenges;
25+
import io.supertokens.cronjobs.cleanupWebauthnExpiredData.CleanUpWebauthNExpiredDataCron;
2526
import io.supertokens.cronjobs.deleteExpiredAccessTokenSigningKeys.DeleteExpiredAccessTokenSigningKeys;
2627
import io.supertokens.cronjobs.deleteExpiredDashboardSessions.DeleteExpiredDashboardSessions;
2728
import io.supertokens.cronjobs.deleteExpiredEmailVerificationTokens.DeleteExpiredEmailVerificationTokens;
@@ -265,6 +266,8 @@ private void init() throws IOException, StorageQueryException {
265266

266267
Cronjobs.addCronjob(this, CleanupOAuthSessionsAndChallenges.init(this, uniqueUserPoolIdsTenants));
267268

269+
Cronjobs.addCronjob(this, CleanUpWebauthNExpiredDataCron.init(this, uniqueUserPoolIdsTenants));
270+
268271
// this is to ensure tenantInfos are in sync for the new cron job as well
269272
MultitenancyHelper.getInstance(this).refreshCronjobs();
270273

0 commit comments

Comments
 (0)