@flowerforce/flowerbase 1.2.1-beta.2 → 1.2.1-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -6
- package/dist/auth/controller.d.ts.map +1 -1
- package/dist/auth/controller.js +55 -4
- package/dist/auth/plugins/jwt.d.ts.map +1 -1
- package/dist/auth/plugins/jwt.js +52 -6
- package/dist/auth/providers/anon-user/controller.d.ts +8 -0
- package/dist/auth/providers/anon-user/controller.d.ts.map +1 -0
- package/dist/auth/providers/anon-user/controller.js +90 -0
- package/dist/auth/providers/anon-user/dtos.d.ts +10 -0
- package/dist/auth/providers/anon-user/dtos.d.ts.map +1 -0
- package/dist/auth/providers/anon-user/dtos.js +2 -0
- package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
- package/dist/auth/providers/custom-function/controller.js +35 -25
- package/dist/auth/providers/custom-function/dtos.d.ts +4 -1
- package/dist/auth/providers/custom-function/dtos.d.ts.map +1 -1
- package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -1
- package/dist/auth/providers/local-userpass/controller.js +159 -73
- package/dist/auth/providers/local-userpass/dtos.d.ts +17 -2
- package/dist/auth/providers/local-userpass/dtos.d.ts.map +1 -1
- package/dist/auth/utils.d.ts +76 -14
- package/dist/auth/utils.d.ts.map +1 -1
- package/dist/auth/utils.js +55 -61
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +16 -4
- package/dist/features/functions/controller.d.ts.map +1 -1
- package/dist/features/functions/controller.js +31 -12
- package/dist/features/functions/dtos.d.ts +3 -0
- package/dist/features/functions/dtos.d.ts.map +1 -1
- package/dist/features/functions/interface.d.ts +3 -0
- package/dist/features/functions/interface.d.ts.map +1 -1
- package/dist/features/functions/utils.d.ts +3 -2
- package/dist/features/functions/utils.d.ts.map +1 -1
- package/dist/features/functions/utils.js +19 -7
- package/dist/features/triggers/index.d.ts.map +1 -1
- package/dist/features/triggers/index.js +49 -7
- package/dist/features/triggers/interface.d.ts +1 -0
- package/dist/features/triggers/interface.d.ts.map +1 -1
- package/dist/features/triggers/utils.d.ts.map +1 -1
- package/dist/features/triggers/utils.js +67 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -13
- package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.js +72 -2
- package/dist/services/mongodb-atlas/model.d.ts +3 -2
- package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/utils.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/utils.js +3 -1
- package/dist/shared/handleUserRegistration.d.ts.map +1 -1
- package/dist/shared/handleUserRegistration.js +66 -1
- package/dist/shared/models/handleUserRegistration.model.d.ts +2 -1
- package/dist/shared/models/handleUserRegistration.model.d.ts.map +1 -1
- package/dist/shared/models/handleUserRegistration.model.js +1 -0
- package/dist/utils/context/helpers.d.ts +6 -6
- package/dist/utils/context/helpers.d.ts.map +1 -1
- package/dist/utils/context/index.d.ts +1 -1
- package/dist/utils/context/index.d.ts.map +1 -1
- package/dist/utils/context/index.js +176 -9
- package/dist/utils/context/interface.d.ts +1 -1
- package/dist/utils/context/interface.d.ts.map +1 -1
- package/dist/utils/crypto/index.d.ts +1 -0
- package/dist/utils/crypto/index.d.ts.map +1 -1
- package/dist/utils/crypto/index.js +6 -2
- package/dist/utils/initializer/exposeRoutes.js +1 -1
- package/dist/utils/initializer/registerPlugins.d.ts.map +1 -1
- package/dist/utils/initializer/registerPlugins.js +12 -4
- package/dist/utils/roles/helpers.js +2 -1
- package/dist/utils/rules-matcher/utils.d.ts.map +1 -1
- package/dist/utils/rules-matcher/utils.js +3 -0
- package/package.json +1 -2
- package/src/auth/controller.ts +71 -5
- package/src/auth/plugins/jwt.test.ts +93 -0
- package/src/auth/plugins/jwt.ts +67 -8
- package/src/auth/providers/anon-user/controller.ts +91 -0
- package/src/auth/providers/anon-user/dtos.ts +10 -0
- package/src/auth/providers/custom-function/controller.ts +40 -31
- package/src/auth/providers/custom-function/dtos.ts +5 -1
- package/src/auth/providers/local-userpass/controller.ts +211 -101
- package/src/auth/providers/local-userpass/dtos.ts +20 -2
- package/src/auth/utils.ts +66 -83
- package/src/constants.ts +14 -2
- package/src/features/functions/controller.ts +42 -12
- package/src/features/functions/dtos.ts +3 -0
- package/src/features/functions/interface.ts +3 -0
- package/src/features/functions/utils.ts +29 -8
- package/src/features/triggers/index.ts +44 -1
- package/src/features/triggers/interface.ts +1 -0
- package/src/features/triggers/utils.ts +89 -37
- package/src/index.ts +49 -13
- package/src/services/mongodb-atlas/__tests__/findOneAndUpdate.test.ts +95 -0
- package/src/services/mongodb-atlas/index.ts +665 -567
- package/src/services/mongodb-atlas/model.ts +16 -3
- package/src/services/mongodb-atlas/utils.ts +3 -0
- package/src/shared/handleUserRegistration.ts +83 -2
- package/src/shared/models/handleUserRegistration.model.ts +2 -1
- package/src/utils/__tests__/registerPlugins.test.ts +5 -1
- package/src/utils/context/index.ts +238 -18
- package/src/utils/context/interface.ts +1 -1
- package/src/utils/crypto/index.ts +5 -1
- package/src/utils/initializer/exposeRoutes.ts +1 -1
- package/src/utils/initializer/registerPlugins.ts +8 -0
- package/src/utils/roles/helpers.ts +3 -2
- package/src/utils/rules-matcher/utils.ts +3 -0
package/src/auth/plugins/jwt.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import fastifyJwt from '@fastify/jwt'
|
|
2
2
|
import fp from 'fastify-plugin'
|
|
3
3
|
import { Document, ObjectId, WithId } from 'mongodb'
|
|
4
|
+
import { AUTH_CONFIG, DB_NAME, DEFAULT_CONFIG } from '../../constants'
|
|
4
5
|
|
|
5
6
|
type Options = {
|
|
6
7
|
secret: string
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
type JwtAccessWithTimestamp = {
|
|
11
|
+
typ: 'access'
|
|
12
|
+
sub: string
|
|
13
|
+
iat?: number
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
/**
|
|
10
17
|
* This module is a Fastify plugin that sets up JWT-based authentication and token creation.
|
|
11
18
|
* It registers JWT authentication, and provides methods to create access and refresh tokens.
|
|
@@ -25,20 +32,72 @@ export default fp(async function (fastify, opts: Options) {
|
|
|
25
32
|
try {
|
|
26
33
|
await request.jwtVerify()
|
|
27
34
|
} catch (err) {
|
|
28
|
-
|
|
29
|
-
reply.send(
|
|
35
|
+
fastify.log.warn({ err }, 'JWT authentication failed')
|
|
36
|
+
reply.code(401).send({ message: 'Unauthorized' })
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (request.user?.typ !== 'access') {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const db = fastify.mongo?.client?.db(DB_NAME)
|
|
45
|
+
if (!db) {
|
|
46
|
+
fastify.log.warn('Mongo client unavailable while checking logout state')
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!request.user.sub) {
|
|
51
|
+
reply.code(401).send({ message: 'Unauthorized' })
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let authUser
|
|
56
|
+
try {
|
|
57
|
+
authUser = await db
|
|
58
|
+
.collection<Document>(AUTH_CONFIG.authCollection)
|
|
59
|
+
.findOne({ _id: new ObjectId(request.user.sub) })
|
|
60
|
+
} catch (err) {
|
|
61
|
+
fastify.log.warn({ err }, 'Failed to lookup user during JWT authentication')
|
|
62
|
+
reply.code(401).send({ message: 'Unauthorized' })
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!authUser) {
|
|
67
|
+
reply.code(401).send({ message: 'Unauthorized' })
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const lastLogoutAt = authUser.lastLogoutAt ? new Date(authUser.lastLogoutAt) : null
|
|
72
|
+
const accessUser = request.user as JwtAccessWithTimestamp
|
|
73
|
+
const rawIssuedAt = accessUser.iat
|
|
74
|
+
const issuedAt =
|
|
75
|
+
typeof rawIssuedAt === 'number'
|
|
76
|
+
? rawIssuedAt
|
|
77
|
+
: typeof rawIssuedAt === 'string'
|
|
78
|
+
? Number(rawIssuedAt)
|
|
79
|
+
: undefined
|
|
80
|
+
if (
|
|
81
|
+
lastLogoutAt &&
|
|
82
|
+
!Number.isNaN(lastLogoutAt.getTime()) &&
|
|
83
|
+
typeof issuedAt === 'number' &&
|
|
84
|
+
!Number.isNaN(issuedAt) &&
|
|
85
|
+
lastLogoutAt.getTime() >= issuedAt * 1000
|
|
86
|
+
) {
|
|
87
|
+
reply.code(401).send({ message: 'Unauthorized' })
|
|
88
|
+
return
|
|
30
89
|
}
|
|
31
90
|
})
|
|
32
91
|
|
|
33
92
|
fastify.decorate('createAccessToken', function (user: WithId<Document>) {
|
|
34
93
|
const id = user._id.toString()
|
|
35
|
-
const userDataId = user.user_data._id.toString()
|
|
94
|
+
// const userDataId = user.user_data._id.toString()
|
|
36
95
|
|
|
37
96
|
const user_data = {
|
|
38
|
-
|
|
39
|
-
|
|
97
|
+
...user.user_data,
|
|
98
|
+
_id: id,
|
|
99
|
+
id: id,
|
|
40
100
|
email: user.email,
|
|
41
|
-
...user.user_data
|
|
42
101
|
}
|
|
43
102
|
|
|
44
103
|
return this.jwt.sign(
|
|
@@ -52,7 +111,7 @@ export default fp(async function (fastify, opts: Options) {
|
|
|
52
111
|
{
|
|
53
112
|
iss: BAAS_ID,
|
|
54
113
|
jti: BAAS_ID,
|
|
55
|
-
sub:
|
|
114
|
+
sub: id,
|
|
56
115
|
expiresIn: '300m'
|
|
57
116
|
}
|
|
58
117
|
)
|
|
@@ -66,7 +125,7 @@ export default fp(async function (fastify, opts: Options) {
|
|
|
66
125
|
},
|
|
67
126
|
{
|
|
68
127
|
sub: user._id.toJSON(),
|
|
69
|
-
expiresIn:
|
|
128
|
+
expiresIn: `${DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS}d`
|
|
70
129
|
}
|
|
71
130
|
)
|
|
72
131
|
})
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { FastifyInstance } from 'fastify'
|
|
2
|
+
import { AUTH_CONFIG, DB_NAME, DEFAULT_CONFIG } from '../../../constants'
|
|
3
|
+
import { hashToken } from '../../../utils/crypto'
|
|
4
|
+
import { PROVIDER } from '../../../shared/models/handleUserRegistration.model'
|
|
5
|
+
import { AUTH_ENDPOINTS } from '../../utils'
|
|
6
|
+
import { LoginDto } from './dtos'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Controller for handling anonymous user login.
|
|
10
|
+
* @testable
|
|
11
|
+
* @param {FastifyInstance} app - The Fastify instance.
|
|
12
|
+
*/
|
|
13
|
+
export async function anonUserController(app: FastifyInstance) {
|
|
14
|
+
const db = app.mongo.client.db(DB_NAME)
|
|
15
|
+
const { authCollection, refreshTokensCollection, providers } = AUTH_CONFIG
|
|
16
|
+
const refreshTokenTtlMs = DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS * 24 * 60 * 60 * 1000
|
|
17
|
+
const anonUserTtlSeconds = DEFAULT_CONFIG.ANON_USER_TTL_SECONDS
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
await db.collection(authCollection!).createIndex(
|
|
21
|
+
{ createdAt: 1 },
|
|
22
|
+
{
|
|
23
|
+
expireAfterSeconds: anonUserTtlSeconds,
|
|
24
|
+
partialFilterExpression: { 'identities.provider_type': PROVIDER.ANON_USER }
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('Failed to ensure anonymous user TTL index', error)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
app.post<LoginDto>(
|
|
32
|
+
AUTH_ENDPOINTS.LOGIN,
|
|
33
|
+
async function () {
|
|
34
|
+
const anonProvider = providers?.['anon-user']
|
|
35
|
+
if (anonProvider?.disabled) {
|
|
36
|
+
throw new Error('Anonymous authentication disabled')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const now = new Date()
|
|
40
|
+
const insertResult = await db.collection(authCollection!).insertOne({
|
|
41
|
+
status: 'confirmed',
|
|
42
|
+
createdAt: now,
|
|
43
|
+
custom_data: {},
|
|
44
|
+
identities: [
|
|
45
|
+
{
|
|
46
|
+
provider_type: PROVIDER.ANON_USER,
|
|
47
|
+
provider_data: {}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const userId = insertResult.insertedId
|
|
53
|
+
await db.collection(authCollection!).updateOne(
|
|
54
|
+
{ _id: userId },
|
|
55
|
+
{
|
|
56
|
+
$set: {
|
|
57
|
+
identities: [
|
|
58
|
+
{
|
|
59
|
+
id: userId.toString(),
|
|
60
|
+
provider_id: userId.toString(),
|
|
61
|
+
provider_type: PROVIDER.ANON_USER,
|
|
62
|
+
provider_data: {}
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
const currentUserData = {
|
|
70
|
+
_id: userId,
|
|
71
|
+
user_data: {}
|
|
72
|
+
}
|
|
73
|
+
const refreshToken = this.createRefreshToken(currentUserData)
|
|
74
|
+
const refreshTokenHash = hashToken(refreshToken)
|
|
75
|
+
await db.collection(refreshTokensCollection).insertOne({
|
|
76
|
+
userId,
|
|
77
|
+
tokenHash: refreshTokenHash,
|
|
78
|
+
createdAt: now,
|
|
79
|
+
expiresAt: new Date(Date.now() + refreshTokenTtlMs),
|
|
80
|
+
revokedAt: null
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
access_token: this.createAccessToken(currentUserData),
|
|
85
|
+
refresh_token: refreshToken,
|
|
86
|
+
device_id: '',
|
|
87
|
+
user_id: userId.toString()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
}
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify'
|
|
2
|
-
import { AUTH_CONFIG } from '../../../constants'
|
|
3
|
-
import handleUserRegistration from '../../../shared/handleUserRegistration'
|
|
4
|
-
import { PROVIDER } from '../../../shared/models/handleUserRegistration.model'
|
|
2
|
+
import { AUTH_CONFIG, DB_NAME, DEFAULT_CONFIG } from '../../../constants'
|
|
5
3
|
import { StateManager } from '../../../state'
|
|
6
4
|
import { GenerateContext } from '../../../utils/context'
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} from '../../utils'
|
|
11
|
-
import {
|
|
12
|
-
LoginDto
|
|
13
|
-
} from './dtos'
|
|
5
|
+
import { hashToken } from '../../../utils/crypto'
|
|
6
|
+
import { AUTH_ENDPOINTS } from '../../utils'
|
|
7
|
+
import { LoginDto } from './dtos'
|
|
14
8
|
import { LOGIN_SCHEMA } from './schema'
|
|
15
9
|
|
|
16
10
|
/**
|
|
@@ -22,6 +16,9 @@ export async function customFunctionController(app: FastifyInstance) {
|
|
|
22
16
|
|
|
23
17
|
const functionsList = StateManager.select('functions')
|
|
24
18
|
const services = StateManager.select('services')
|
|
19
|
+
const db = app.mongo.client.db(DB_NAME)
|
|
20
|
+
const { authCollection, refreshTokensCollection } = AUTH_CONFIG
|
|
21
|
+
const refreshTokenTtlMs = DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS * 24 * 60 * 60 * 1000
|
|
25
22
|
|
|
26
23
|
/**
|
|
27
24
|
* Endpoint for user login.
|
|
@@ -35,7 +32,7 @@ export async function customFunctionController(app: FastifyInstance) {
|
|
|
35
32
|
{
|
|
36
33
|
schema: LOGIN_SCHEMA
|
|
37
34
|
},
|
|
38
|
-
async function (req) {
|
|
35
|
+
async function (req, reply) {
|
|
39
36
|
const { providers } = AUTH_CONFIG
|
|
40
37
|
const authFunctionName = providers["custom-function"].authFunctionName
|
|
41
38
|
|
|
@@ -53,7 +50,8 @@ export async function customFunctionController(app: FastifyInstance) {
|
|
|
53
50
|
id
|
|
54
51
|
} = req
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
type CustomFunctionAuthResult = { id?: string }
|
|
54
|
+
const authResult = await GenerateContext({
|
|
57
55
|
args: [
|
|
58
56
|
req.body
|
|
59
57
|
],
|
|
@@ -72,30 +70,41 @@ export async function customFunctionController(app: FastifyInstance) {
|
|
|
72
70
|
ip,
|
|
73
71
|
id
|
|
74
72
|
}
|
|
75
|
-
})
|
|
73
|
+
}) as CustomFunctionAuthResult
|
|
76
74
|
|
|
77
75
|
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
76
|
+
if (!authResult.id) {
|
|
77
|
+
reply.code(401).send({ message: 'Unauthorized' })
|
|
78
|
+
return
|
|
79
|
+
}
|
|
83
80
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return {
|
|
91
|
-
access_token: this.createAccessToken(currentUserData),
|
|
92
|
-
refresh_token: this.createRefreshToken(currentUserData),
|
|
93
|
-
device_id: '',
|
|
94
|
-
user_id: user.insertedId.toString()
|
|
95
|
-
}
|
|
81
|
+
const authUser = await db.collection(authCollection!).findOne({ email: authResult.id })
|
|
82
|
+
if (!authUser) {
|
|
83
|
+
reply.code(401).send({ message: 'Unauthorized' })
|
|
84
|
+
return
|
|
96
85
|
}
|
|
97
86
|
|
|
98
|
-
|
|
87
|
+
const currentUserData = {
|
|
88
|
+
_id: authUser._id,
|
|
89
|
+
user_data: {
|
|
90
|
+
_id: authUser._id
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const refreshToken = this.createRefreshToken(currentUserData)
|
|
94
|
+
const refreshTokenHash = hashToken(refreshToken)
|
|
95
|
+
await db.collection(refreshTokensCollection).insertOne({
|
|
96
|
+
userId: authUser._id,
|
|
97
|
+
tokenHash: refreshTokenHash,
|
|
98
|
+
createdAt: new Date(),
|
|
99
|
+
expiresAt: new Date(Date.now() + refreshTokenTtlMs),
|
|
100
|
+
revokedAt: null
|
|
101
|
+
})
|
|
102
|
+
return {
|
|
103
|
+
access_token: this.createAccessToken(currentUserData),
|
|
104
|
+
refresh_token: refreshToken,
|
|
105
|
+
device_id: '',
|
|
106
|
+
user_id: authUser._id.toString()
|
|
107
|
+
}
|
|
99
108
|
}
|
|
100
109
|
)
|
|
101
110
|
|