Skip to content

Commit 6e00e57

Browse files
committed
feat: frontend passkey/auth0 custom provider integration
1 parent 72ae9ca commit 6e00e57

File tree

33 files changed

+710
-127
lines changed

33 files changed

+710
-127
lines changed

apps/api/api-types/index.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ export type {
1414
PasskeyFinalizeRegistrationApi,
1515
PasskeyStartLoginApi,
1616
PasskeyFinalizeLoginApi,
17-
} from '../dist/schemas/index.js';
17+
UserInfoApi,
18+
} from '../src/schemas/index';

apps/api/src/modules/project/handlers/project.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Project, User} from '@codeimage/prisma-models';
2-
import {HttpError, HttpErrors} from '@fastify/sensible/lib/httpError.js';
2+
import {HttpErrors} from '@fastify/sensible/lib/httpError.js';
33
import {createProjectRequestMapper} from '../mapper/create-project-mapper.js';
44
import {createCompleteProjectGetByIdResponseMapper} from '../mapper/get-project-by-id-mapper.js';
55
import {ProjectRepository} from '../repository/index.js';
@@ -89,7 +89,7 @@ export function makeProjectService(
8989
try {
9090
const project = await repository.findById(projectId);
9191
if (!project) {
92-
throw {name: 'NotFoundError'} as HttpError;
92+
throw {name: 'NotFoundError'} as HttpErrors['HttpError'];
9393
}
9494
return this.createNewProject(user.id, {
9595
name: newName ?? project.name,
@@ -99,7 +99,7 @@ export function makeProjectService(
9999
terminal: project.terminal,
100100
});
101101
} catch (e) {
102-
const error = e as HttpError;
102+
const error = e as HttpErrors['HttpError'];
103103
if (error && error.name === 'NotFoundError') {
104104
throw httpErrors.notFound(
105105
`Cannot clone project with id ${projectId} since it does not exists`,

apps/api/src/plugins/errorHandler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import {HttpError} from '@fastify/sensible/lib/httpError.js';
1+
import {HttpErrors} from '@fastify/sensible/lib/httpError.js';
22
import fp from 'fastify-plugin';
33
import {NotFoundEntityException} from '../common/exceptions/NotFoundEntityException.js';
44

55
export default fp(
66
async fastify => {
77
fastify.setErrorHandler((error, request, reply) => {
8-
let httpError: HttpError | null = null;
8+
let httpError: HttpErrors['HttpError'] | null = null;
99

1010
if (error.statusCode) {
1111
httpError = fastify.httpErrors.createError(

apps/api/src/routes/v1/passkey/deleteCredential.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import {FastifyPluginAsyncTypebox} from '@fastify/type-provider-typebox';
22
import {Type} from '@sinclair/typebox';
33

44
const route: FastifyPluginAsyncTypebox = async fastify => {
5+
fastify.addHook(
6+
'preValidation',
7+
fastify.authorize({mustBeAuthenticated: true}),
8+
);
59
fastify.delete(
610
'/credentials',
711
{
@@ -10,11 +14,6 @@ const route: FastifyPluginAsyncTypebox = async fastify => {
1014
credentialId: Type.String(),
1115
}),
1216
},
13-
preValidation: function (request, reply, done) {
14-
return fastify
15-
.auth([fastify.authenticate, fastify.verifyHankoPasskey])
16-
.apply(this, [request, reply, done]);
17-
},
1817
},
1918
async request => {
2019
return fastify.passkeysApi.credential(request.params.credentialId);

apps/api/src/routes/v1/passkey/finalizeRegistration.ts

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,29 +47,26 @@ const schema = {
4747
export type PasskeyFinalizeRegistrationApi = GetApiTypes<typeof schema>;
4848

4949
const route: FastifyPluginAsyncTypebox = async fastify => {
50-
fastify.post(
51-
'/finalize-registration',
52-
{
53-
schema,
54-
preValidation: (req, reply) => fastify.authorize(req, reply),
55-
},
56-
async request => {
57-
// const {appUser} = request;
58-
return fastify.passkeysApi.registration.finalize({
59-
rawId: request.body.rawId,
60-
type: request.body.type,
61-
transports: request.body.transports,
62-
authenticatorAttachment: request.body.authenticatorAttachment,
63-
id: request.body.id,
64-
clientExtensionResults: request.body.clientExtensionResults,
65-
response: {
66-
transports: request.body.response.transports,
67-
clientDataJSON: request.body.response.clientDataJSON,
68-
attestationObject: request.body.response.attestationObject,
69-
},
70-
});
71-
},
50+
fastify.addHook(
51+
'preValidation',
52+
fastify.authorize({mustBeAuthenticated: true}),
7253
);
54+
fastify.post('/finalize-registration', {schema}, async request => {
55+
// const {appUser} = request;
56+
return fastify.passkeysApi.registration.finalize({
57+
rawId: request.body.rawId,
58+
type: request.body.type,
59+
transports: request.body.transports,
60+
authenticatorAttachment: request.body.authenticatorAttachment,
61+
id: request.body.id,
62+
clientExtensionResults: request.body.clientExtensionResults,
63+
response: {
64+
transports: request.body.response.transports,
65+
clientDataJSON: request.body.response.clientDataJSON,
66+
attestationObject: request.body.response.attestationObject,
67+
},
68+
});
69+
});
7370
};
7471

7572
export default route;
Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
import {FastifyPluginAsyncTypebox} from '@fastify/type-provider-typebox';
22

33
const route: FastifyPluginAsyncTypebox = async fastify => {
4-
fastify.get(
5-
'/credentials',
6-
{
7-
preValidation: (request, reply) => fastify.authenticate(request, reply),
8-
},
9-
async () => {
10-
return fetch(
11-
`https://passkeys.hanko.io/${fastify.config.HANKO_PASSKEYS_TENANT_ID}/credentials?user_id=4676fe25-3660-4c0d-b89e-34b177e759f0`,
12-
{
13-
headers: {
14-
apikey: fastify.config.HANKO_PASSKEYS_API_KEY,
15-
},
16-
},
17-
)
18-
.then(s => s.json())
19-
.then(s => {
20-
console.log(s);
21-
return s;
22-
});
23-
},
4+
fastify.addHook(
5+
'preValidation',
6+
fastify.authorize({mustBeAuthenticated: true}),
247
);
8+
fastify.get('/credentials', {}, async () => {
9+
return fetch(
10+
`https://passkeys.hanko.io/${fastify.config.HANKO_PASSKEYS_TENANT_ID}/credentials?user_id=4676fe25-3660-4c0d-b89e-34b177e759f0`,
11+
{
12+
headers: {
13+
apikey: fastify.config.HANKO_PASSKEYS_API_KEY,
14+
},
15+
},
16+
)
17+
.then(s => s.json())
18+
.then(s => {
19+
console.log(s);
20+
return s;
21+
});
22+
});
2523
};
2624

2725
export default route;

apps/api/src/routes/v1/passkey/startRegistration.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,20 @@ const schema = {
5858
export type PasskeyStartRegistrationApi = GetApiTypes<typeof schema>;
5959

6060
const route: FastifyPluginAsyncTypebox = async fastify => {
61-
fastify.post(
62-
'/registration',
63-
{
64-
preValidation: (req, reply) => fastify.authorize(req, reply),
65-
schema,
66-
},
67-
async request => {
68-
const {appUser} = request;
69-
fastify.log.info(
70-
`Init passkey registration for user with id ${appUser.id}`,
71-
);
72-
return fastify.passkeysApi.registration.initialize({
73-
userId: appUser.id,
74-
username: appUser.email,
75-
});
76-
},
61+
fastify.addHook(
62+
'preValidation',
63+
fastify.authorize({mustBeAuthenticated: true}),
7764
);
65+
fastify.post('/registration', {schema}, async request => {
66+
const {appUser} = request;
67+
fastify.log.info(
68+
`Init passkey registration for user with id ${appUser.id}`,
69+
);
70+
return fastify.passkeysApi.registration.initialize({
71+
userId: appUser.id,
72+
username: appUser.email,
73+
});
74+
});
7875
};
7976

8077
export default route;

apps/api/src/routes/v1/user/info.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,38 @@
11
import {FastifyPluginAsyncTypebox} from '@fastify/type-provider-typebox';
2+
import {Static, Type} from '@sinclair/typebox';
3+
import {FastifySchema} from 'fastify';
4+
import {GetApiTypes} from '../../../common/types/extract-api-types.js';
5+
6+
const UserInfoSchema = Type.Object({
7+
id: Type.String(),
8+
user_id: Type.String(),
9+
email: Type.String(),
10+
created_at: Type.String({format: 'date-time'}),
11+
email_verified: Type.Boolean(),
12+
picture: Type.String(),
13+
});
14+
15+
const schema = {
16+
response: {
17+
200: UserInfoSchema,
18+
},
19+
} satisfies FastifySchema;
20+
21+
export type UserInfoApi = GetApiTypes<typeof schema>;
222

323
const route: FastifyPluginAsyncTypebox = async fastify => {
424
fastify.addHook(
525
'preValidation',
626
fastify.authorize({mustBeAuthenticated: true}),
727
);
8-
fastify.get('/info', {}, async request => {
28+
fastify.get('/info', {schema}, async request => {
929
const response = await fastify.auth0Management.usersByEmail.getByEmail({
1030
email: request.appUser.email,
1131
fields: 'user_id,email,created_at,email_verified,picture',
1232
});
13-
return {...response.data[0], id: request.appUser.id};
33+
return {...response.data[0], id: request.appUser.id} as Static<
34+
typeof UserInfoSchema
35+
>;
1436
});
1537
};
1638

apps/api/src/schemas/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ export type {PasskeyStartRegistrationApi} from '../routes/v1/passkey/startRegist
1515
export type {PasskeyFinalizeRegistrationApi} from '../routes/v1/passkey/finalizeRegistration.js';
1616
export type {PasskeyStartLoginApi} from '../routes/v1/passkey/startLogin.js';
1717
export type {PasskeyFinalizeLoginApi} from '../routes/v1/passkey/finalizeLogin.js';
18+
export type {UserInfoApi} from '../routes/v1/user/info.js';

apps/codeimage/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
"@floating-ui/core": "^1.2.2",
7272
"@floating-ui/dom": "^1.2.3",
7373
"@formatjs/intl-relativetimeformat": "11.1.4",
74+
"@github/webauthn-json": "2.1.1",
7475
"@kobalte/core": "^0.11.0",
7576
"@kobalte/utils": "^0.9.0",
7677
"@kobalte/vanilla-extract": "^0.4.0",
@@ -89,6 +90,7 @@
8990
"@solid-primitives/platform": "^0.0.101",
9091
"@solid-primitives/props": "^2.2.2",
9192
"@solid-primitives/resize-observer": "^2.0.11",
93+
"@solid-primitives/storage": "2.1.1",
9294
"@solid-primitives/utils": "^6.0.0",
9395
"@solidjs/router": "^0.8.2",
9496
"@thisbeyond/solid-dnd": "0.7.2",
@@ -100,6 +102,7 @@
100102
"downloadjs": "^1.4.7",
101103
"idb-keyval": "^6.2.0",
102104
"inter-ui": "^3.19.3",
105+
"jwt-decode": "4.0.0",
103106
"modern-normalize": "^1.1.0",
104107
"motion": "^10.15.5",
105108
"polished": "^4.2.2",

0 commit comments

Comments
 (0)