@appweaver/core 1.0.0
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/LICENSE +1 -0
- package/README.md +7 -0
- package/app/application.d.ts +43 -0
- package/app/application.js +112 -0
- package/app/create-app.d.ts +32 -0
- package/app/create-app.js +38 -0
- package/app/index.d.ts +2 -0
- package/app/index.js +18 -0
- package/app/lifecycle-manager.d.ts +20 -0
- package/app/lifecycle-manager.js +42 -0
- package/app/load-modules.d.ts +10 -0
- package/app/load-modules.js +50 -0
- package/app/load-providers.d.ts +8 -0
- package/app/load-providers.js +33 -0
- package/cache/cache-service.d.ts +72 -0
- package/cache/cache-service.js +202 -0
- package/cache/cache.d.ts +12 -0
- package/cache/cache.js +184 -0
- package/cache/caching.d.ts +8 -0
- package/cache/caching.js +72 -0
- package/cache/eviction/eviction-index.d.ts +35 -0
- package/cache/eviction/eviction-index.js +2 -0
- package/cache/eviction/fifo-eviction-index.d.ts +7 -0
- package/cache/eviction/fifo-eviction-index.js +28 -0
- package/cache/eviction/index.d.ts +4 -0
- package/cache/eviction/index.js +20 -0
- package/cache/eviction/lfu-eviction-index.d.ts +8 -0
- package/cache/eviction/lfu-eviction-index.js +82 -0
- package/cache/eviction/lru-eviction-index.d.ts +8 -0
- package/cache/eviction/lru-eviction-index.js +81 -0
- package/cache/index.d.ts +2 -0
- package/cache/index.js +18 -0
- package/cache/memory-cache.d.ts +4 -0
- package/cache/memory-cache.js +12 -0
- package/cache/redis-cache.d.ts +4 -0
- package/cache/redis-cache.js +12 -0
- package/context/context.d.ts +2 -0
- package/context/context.js +13 -0
- package/context/dependency-injection.d.ts +98 -0
- package/context/dependency-injection.js +276 -0
- package/context/index.d.ts +2 -0
- package/context/index.js +18 -0
- package/database/create-client.d.ts +11 -0
- package/database/create-client.js +142 -0
- package/database/index.d.ts +1 -0
- package/database/index.js +17 -0
- package/database/prisma-database.d.ts +15 -0
- package/database/prisma-database.js +53 -0
- package/errors/error-handler.d.ts +3 -0
- package/errors/error-handler.js +24 -0
- package/errors/error-schema.d.ts +32 -0
- package/errors/error-schema.js +31 -0
- package/errors/http-error.d.ts +21 -0
- package/errors/http-error.js +33 -0
- package/errors/index.d.ts +3 -0
- package/errors/index.js +19 -0
- package/events/node-events.d.ts +7 -0
- package/events/node-events.js +39 -0
- package/export/export-service.d.ts +19 -0
- package/export/export-service.js +154 -0
- package/export/index.d.ts +1 -0
- package/export/index.js +17 -0
- package/factory/create-model.d.ts +2 -0
- package/factory/create-model.js +366 -0
- package/factory/create-policy.d.ts +2 -0
- package/factory/create-policy.js +16 -0
- package/factory/create-routes.d.ts +2 -0
- package/factory/create-routes.js +21 -0
- package/factory/create-service.d.ts +3 -0
- package/factory/create-service.js +97 -0
- package/factory/index.d.ts +4 -0
- package/factory/index.js +20 -0
- package/health/health-routes.d.ts +2 -0
- package/health/health-routes.js +36 -0
- package/health/health-schema.d.ts +45 -0
- package/health/health-schema.js +52 -0
- package/health/health-service.d.ts +38 -0
- package/health/health-service.js +82 -0
- package/health/index.d.ts +2 -0
- package/health/index.js +18 -0
- package/index.d.ts +15 -0
- package/index.js +31 -0
- package/mailer/email-service.d.ts +36 -0
- package/mailer/email-service.js +89 -0
- package/mailer/index.d.ts +1 -0
- package/mailer/index.js +17 -0
- package/mailer/json-mailer.d.ts +7 -0
- package/mailer/json-mailer.js +32 -0
- package/mailer/smtp-mailer.d.ts +7 -0
- package/mailer/smtp-mailer.js +43 -0
- package/memory/in-memory.d.ts +24 -0
- package/memory/in-memory.js +164 -0
- package/memory/redis.d.ts +26 -0
- package/memory/redis.js +158 -0
- package/package.json +68 -0
- package/prisma/client/browser.d.ts +34 -0
- package/prisma/client/browser.js +56 -0
- package/prisma/client/client.d.ts +53 -0
- package/prisma/client/client.js +70 -0
- package/prisma/client/commonInputTypes.d.ts +395 -0
- package/prisma/client/commonInputTypes.js +11 -0
- package/prisma/client/enums.d.ts +1 -0
- package/prisma/client/enums.js +11 -0
- package/prisma/client/internal/class.d.ts +187 -0
- package/prisma/client/internal/class.js +85 -0
- package/prisma/client/internal/prismaNamespace.d.ts +987 -0
- package/prisma/client/internal/prismaNamespace.js +193 -0
- package/prisma/client/internal/prismaNamespaceBrowser.d.ts +125 -0
- package/prisma/client/internal/prismaNamespaceBrowser.js +164 -0
- package/prisma/client/models/ApiKey.d.ts +1167 -0
- package/prisma/client/models/ApiKey.js +2 -0
- package/prisma/client/models/File.d.ts +1248 -0
- package/prisma/client/models/File.js +2 -0
- package/prisma/client/models/OneTimeToken.d.ts +1083 -0
- package/prisma/client/models/OneTimeToken.js +2 -0
- package/prisma/client/models/Permission.d.ts +1209 -0
- package/prisma/client/models/Permission.js +2 -0
- package/prisma/client/models/Role.d.ts +1209 -0
- package/prisma/client/models/Role.js +2 -0
- package/prisma/client/models/Seeder.d.ts +1023 -0
- package/prisma/client/models/Seeder.js +2 -0
- package/prisma/client/models.d.ts +7 -0
- package/prisma/client/models.js +2 -0
- package/queue/bull-queue.d.ts +37 -0
- package/queue/bull-queue.js +207 -0
- package/queue/memory-queue.d.ts +54 -0
- package/queue/memory-queue.js +283 -0
- package/resource/index.d.ts +4 -0
- package/resource/index.js +20 -0
- package/resource/resource-loader.d.ts +26 -0
- package/resource/resource-loader.js +161 -0
- package/resource/resource-routes.d.ts +3 -0
- package/resource/resource-routes.js +174 -0
- package/resource/resource-schema.d.ts +33 -0
- package/resource/resource-schema.js +186 -0
- package/resource/resource-service.d.ts +89 -0
- package/resource/resource-service.js +772 -0
- package/resources.d.ts +9 -0
- package/resources.js +24 -0
- package/scheduler/cron-scheduler.d.ts +12 -0
- package/scheduler/cron-scheduler.js +73 -0
- package/security/account/account-routes.d.ts +2 -0
- package/security/account/account-routes.js +104 -0
- package/security/account/account-schema.d.ts +290 -0
- package/security/account/account-schema.js +166 -0
- package/security/account/account-service.d.ts +88 -0
- package/security/account/account-service.js +218 -0
- package/security/account/index.d.ts +3 -0
- package/security/account/index.js +19 -0
- package/security/api-key/api-key-auth.d.ts +4 -0
- package/security/api-key/api-key-auth.js +66 -0
- package/security/api-key/index.d.ts +1 -0
- package/security/api-key/index.js +17 -0
- package/security/auth-routes.d.ts +2 -0
- package/security/auth-routes.js +80 -0
- package/security/auth-schema.d.ts +215 -0
- package/security/auth-schema.js +129 -0
- package/security/auth-service.d.ts +117 -0
- package/security/auth-service.js +272 -0
- package/security/auth.d.ts +3 -0
- package/security/auth.js +127 -0
- package/security/basic/basic-auth.d.ts +4 -0
- package/security/basic/basic-auth.js +32 -0
- package/security/basic/index.d.ts +1 -0
- package/security/basic/index.js +17 -0
- package/security/create-auth-resources.d.ts +6 -0
- package/security/create-auth-resources.js +129 -0
- package/security/helper.d.ts +127 -0
- package/security/helper.js +329 -0
- package/security/index.d.ts +4 -0
- package/security/index.js +20 -0
- package/security/jwt/index.d.ts +2 -0
- package/security/jwt/index.js +18 -0
- package/security/jwt/jwt-auth.d.ts +4 -0
- package/security/jwt/jwt-auth.js +57 -0
- package/security/jwt/jwt-keys.d.ts +7 -0
- package/security/jwt/jwt-keys.js +43 -0
- package/security/oauth2/create-oauth2-plugin.d.ts +17 -0
- package/security/oauth2/create-oauth2-plugin.js +85 -0
- package/security/oauth2/index.d.ts +3 -0
- package/security/oauth2/index.js +19 -0
- package/security/oauth2/oauth2-custom.d.ts +7 -0
- package/security/oauth2/oauth2-custom.js +40 -0
- package/security/oauth2/oauth2-facebook.d.ts +1 -0
- package/security/oauth2/oauth2-facebook.js +30 -0
- package/security/oauth2/oauth2-google.d.ts +1 -0
- package/security/oauth2/oauth2-google.js +28 -0
- package/security/oauth2/oauth2-schema.d.ts +10 -0
- package/security/oauth2/oauth2-schema.js +53 -0
- package/security/recaptcha/index.d.ts +2 -0
- package/security/recaptcha/index.js +18 -0
- package/security/recaptcha/recaptcha-verify.d.ts +1 -0
- package/security/recaptcha/recaptcha-verify.js +33 -0
- package/security/recaptcha/recaptcha.d.ts +2 -0
- package/security/recaptcha/recaptcha.js +22 -0
- package/security/resources/api-key/model.d.ts +2 -0
- package/security/resources/api-key/model.js +63 -0
- package/security/resources/api-key/policy.d.ts +2 -0
- package/security/resources/api-key/policy.js +26 -0
- package/security/resources/api-key/routes.d.ts +2 -0
- package/security/resources/api-key/routes.js +15 -0
- package/security/resources/api-key/service.d.ts +3 -0
- package/security/resources/api-key/service.js +33 -0
- package/security/resources/one-time-token/model.d.ts +2 -0
- package/security/resources/one-time-token/model.js +32 -0
- package/security/resources/permission/model.d.ts +2 -0
- package/security/resources/permission/model.js +16 -0
- package/security/resources/role/model.d.ts +2 -0
- package/security/resources/role/model.js +22 -0
- package/security/store/database-security-store.d.ts +5 -0
- package/security/store/database-security-store.js +51 -0
- package/security/store/redis-security-store.d.ts +5 -0
- package/security/store/redis-security-store.js +32 -0
- package/seeder/create-seeder.d.ts +21 -0
- package/seeder/create-seeder.js +17 -0
- package/seeder/index.d.ts +2 -0
- package/seeder/index.js +18 -0
- package/seeder/resources/seeder/model.d.ts +2 -0
- package/seeder/resources/seeder/model.js +38 -0
- package/seeder/seeder.d.ts +20 -0
- package/seeder/seeder.js +175 -0
- package/server/create-server.d.ts +20 -0
- package/server/create-server.js +160 -0
- package/server/index.d.ts +4 -0
- package/server/index.js +20 -0
- package/server/info-route.d.ts +2 -0
- package/server/info-route.js +18 -0
- package/server/info-schema.d.ts +37 -0
- package/server/info-schema.js +19 -0
- package/server/register-model.d.ts +2 -0
- package/server/register-model.js +12 -0
- package/server/register-plugin.d.ts +2 -0
- package/server/register-plugin.js +13 -0
- package/server/register-route.d.ts +3 -0
- package/server/register-route.js +74 -0
- package/server/schemas.d.ts +3 -0
- package/server/schemas.js +30 -0
- package/server/swagger.d.ts +3 -0
- package/server/swagger.js +122 -0
- package/storage/file-route.d.ts +2 -0
- package/storage/file-route.js +39 -0
- package/storage/file-schema.d.ts +9 -0
- package/storage/file-schema.js +42 -0
- package/storage/file-service.d.ts +81 -0
- package/storage/file-service.js +384 -0
- package/storage/filesystem-storage.d.ts +10 -0
- package/storage/filesystem-storage.js +128 -0
- package/storage/index.d.ts +2 -0
- package/storage/index.js +18 -0
- package/storage/resources/file/model.d.ts +2 -0
- package/storage/resources/file/model.js +75 -0
- package/types/application-context.d.ts +20 -0
- package/types/application-context.js +2 -0
- package/types/auth.d.ts +31 -0
- package/types/auth.js +2 -0
- package/types/fastify-extensions.d.ts +24 -0
- package/types/fastify-extensions.js +4 -0
- package/types/generated.d.ts +193 -0
- package/types/generated.js +3 -0
- package/types/index.d.ts +4 -0
- package/types/index.js +20 -0
- package/types/server.d.ts +4 -0
- package/types/server.js +2 -0
- package/utils/export-util.d.ts +8 -0
- package/utils/export-util.js +25 -0
- package/utils/file-util.d.ts +64 -0
- package/utils/file-util.js +141 -0
- package/utils/index.d.ts +3 -0
- package/utils/index.js +19 -0
- package/utils/schema-util.d.ts +20 -0
- package/utils/schema-util.js +37 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthService = void 0;
|
|
4
|
+
const common_1 = require("@appweaver/common");
|
|
5
|
+
const helper_1 = require("./helper");
|
|
6
|
+
const context_1 = require("../context");
|
|
7
|
+
const cache_1 = require("../cache");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
9
|
+
const AUTH_KEY = 'auth';
|
|
10
|
+
class AuthService {
|
|
11
|
+
/** @internal */
|
|
12
|
+
_securityStore = (0, context_1.inject)(common_1.SecurityStore);
|
|
13
|
+
/** @internal */
|
|
14
|
+
_cacheService = (0, context_1.inject)(cache_1.CacheService);
|
|
15
|
+
/** @internal */
|
|
16
|
+
_authUserService = (0, helper_1.resourceAuthService)();
|
|
17
|
+
/**
|
|
18
|
+
* Finds an authenticated user by their unique identifier.
|
|
19
|
+
*
|
|
20
|
+
* @param {number} id - The unique identifier of the authenticated user to find.
|
|
21
|
+
* @return {Promise<AuthUser | null>} A promise that resolves to the authenticated user object if found, otherwise
|
|
22
|
+
* null.
|
|
23
|
+
*/
|
|
24
|
+
async findById(id) {
|
|
25
|
+
try {
|
|
26
|
+
if (!common_1.config.CACHE_ENABLED || common_1.config.SECURITY_CACHE_TTL < 0) {
|
|
27
|
+
return this._authUserService.find(id);
|
|
28
|
+
}
|
|
29
|
+
const cacheKey = this._cacheService.buildCacheKey({
|
|
30
|
+
baseKey: `${AUTH_KEY}:${id}`,
|
|
31
|
+
modelName: this._authUserService.modelName
|
|
32
|
+
});
|
|
33
|
+
const value = await this._cacheService.getCachedValue(cacheKey);
|
|
34
|
+
if (value) {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
const authUser = await this._authUserService.find(id);
|
|
38
|
+
await this._cacheService.addToCache(cacheKey, authUser, common_1.config.SECURITY_CACHE_TTL);
|
|
39
|
+
return authUser;
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
throw new errors_1.HttpError('Auth user find error', 500, e);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Retrieves a user by their username.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} username - The username to search for.
|
|
49
|
+
* @return {Promise<AuthUser | null>} A promise that resolves to the user object if found, or null if not found.
|
|
50
|
+
*/
|
|
51
|
+
async findByUsername(username) {
|
|
52
|
+
try {
|
|
53
|
+
if (!common_1.config.CACHE_ENABLED || common_1.config.SECURITY_CACHE_TTL < 0) {
|
|
54
|
+
const result = await this._authUserService.query({ email: username });
|
|
55
|
+
return result.items[0] ?? null;
|
|
56
|
+
}
|
|
57
|
+
const cacheKey = this._cacheService.buildCacheKey({
|
|
58
|
+
baseKey: `${AUTH_KEY}:${username}`,
|
|
59
|
+
modelName: this._authUserService.modelName
|
|
60
|
+
});
|
|
61
|
+
const value = await this._cacheService.getCachedValue(cacheKey);
|
|
62
|
+
if (value) {
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
const result = await this._authUserService.query({ email: username });
|
|
66
|
+
const authUser = result.items[0] ?? null;
|
|
67
|
+
if (authUser) {
|
|
68
|
+
await this._cacheService.addToCache(cacheKey, authUser, common_1.config.SECURITY_CACHE_TTL);
|
|
69
|
+
}
|
|
70
|
+
return authUser;
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
throw new errors_1.HttpError('Auth user find error', 500, e);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Updates an authenticated user's information in the system.
|
|
78
|
+
*
|
|
79
|
+
* @param {number} id - The unique identifier of the authenticated user to be updated.
|
|
80
|
+
* @param {Partial<AuthUser> & { password?: string }} data - The partial user data to update, optionally including a
|
|
81
|
+
* password.
|
|
82
|
+
* @return {Promise<AuthUser>} A promise that resolves to the updated authenticated user object.
|
|
83
|
+
*/
|
|
84
|
+
async updateAuthUser(id, data) {
|
|
85
|
+
try {
|
|
86
|
+
return await this._authUserService.update(id, data);
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
throw new errors_1.HttpError('Auth user update error', 500, e);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Registers a new authenticated user based on the provided authentication source, email, and optional password and
|
|
94
|
+
* additional data.
|
|
95
|
+
*
|
|
96
|
+
* @param {AuthSource} source - The authentication source to register the user, e.g., password, google, custom.
|
|
97
|
+
* @param {string} email - The email address of the user to be registered.
|
|
98
|
+
* @param {string} [password] - The optional password for the user, used when the authentication type requires it.
|
|
99
|
+
* @param {Partial<UserAdditionalData>} [data] - Optional additional data related to the user to be stored during
|
|
100
|
+
* registration.
|
|
101
|
+
* @return {Promise<AuthUser>} A promise resolving to the registered authentication user object.
|
|
102
|
+
* @throws {HttpError} Throws an error if the registration process fails.
|
|
103
|
+
*/
|
|
104
|
+
async registerAuthUser(source, email, password, data) {
|
|
105
|
+
try {
|
|
106
|
+
const serviceConfig = this._authUserService[common_1.CONFIG];
|
|
107
|
+
const registrationData = serviceConfig.registrationData(source, email, password, data);
|
|
108
|
+
return this._authUserService.create({
|
|
109
|
+
...registrationData,
|
|
110
|
+
verifiedEmail: source !== common_1.AuthSource.Password
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
throw new errors_1.HttpError('Auth user registration error', 500, e);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Changes the password for the authenticated user.
|
|
119
|
+
*
|
|
120
|
+
* @param {AuthUser} authUser - The authenticated user object containing user details.
|
|
121
|
+
* @param {string} currentPassword - The current password of the authenticated user.
|
|
122
|
+
* @param {string} newPassword - The new password to be set for the authenticated user.
|
|
123
|
+
* @return {Promise<AuthTokens>} A promise that resolves to the new authentication tokens after the password is
|
|
124
|
+
* successfully changed.
|
|
125
|
+
* @throws {HttpError} If the current password does not match the stored password hash.
|
|
126
|
+
*/
|
|
127
|
+
async changePassword(authUser, currentPassword, newPassword) {
|
|
128
|
+
if (!common_1.config.SECURITY_PASSWORD_ENABLED) {
|
|
129
|
+
throw new errors_1.HttpError('Password update is not supported', 403);
|
|
130
|
+
}
|
|
131
|
+
if (!authUser.passwordHash ||
|
|
132
|
+
!(await (0, helper_1.checkPassword)(currentPassword, authUser.passwordHash))) {
|
|
133
|
+
throw new errors_1.HttpError('Auth user current password invalid', 403);
|
|
134
|
+
}
|
|
135
|
+
const result = (0, helper_1.validatePasswordComplexity)(newPassword);
|
|
136
|
+
if (!result.valid) {
|
|
137
|
+
throw new errors_1.HttpError(result.message, 400);
|
|
138
|
+
}
|
|
139
|
+
await this.updateAuthUser(authUser.id, {
|
|
140
|
+
logoutAt: new Date(),
|
|
141
|
+
passwordHash: await (0, helper_1.hashPassword)(newPassword)
|
|
142
|
+
});
|
|
143
|
+
common_1.logger.debug({ id: authUser.id }, 'Password changed successfully');
|
|
144
|
+
return this.login(authUser.email, newPassword);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Authenticates a user by verifying their username and password.
|
|
148
|
+
*
|
|
149
|
+
* @param {string} username - The username of the user attempting to authenticate.
|
|
150
|
+
* @param {string} password - The password of the user attempting to authenticate.
|
|
151
|
+
* @return {Promise<AuthUser>} A promise that resolves to an authenticated user object if the credentials are valid.
|
|
152
|
+
* @throws {HttpError} If the user does not exist, is disabled, or if the provided credentials are invalid.
|
|
153
|
+
*/
|
|
154
|
+
async authenticate(username, password) {
|
|
155
|
+
const authUser = await this.findByUsername(username);
|
|
156
|
+
if (!authUser || !authUser.enabled || !authUser.passwordHash) {
|
|
157
|
+
throw new errors_1.HttpError('Auth user does not exist or is disabled', 400);
|
|
158
|
+
}
|
|
159
|
+
if (!(await (0, helper_1.checkPassword)(password, authUser.passwordHash))) {
|
|
160
|
+
throw new errors_1.HttpError('Invalid user credentials', 401);
|
|
161
|
+
}
|
|
162
|
+
common_1.logger.debug({ id: authUser.id }, 'User authenticated');
|
|
163
|
+
return authUser;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Authorizes a user based on provided authentication details, route configuration, and JWT payload
|
|
167
|
+
* (if JWT auth type is used).
|
|
168
|
+
*
|
|
169
|
+
* @param {AuthUser | null} authUser - The authenticated user object. Can be null if no user is authenticated.
|
|
170
|
+
* @param {string} url - The URL of the resource being accessed.
|
|
171
|
+
* @param {RouteConfig} [routeConfig={}] - The route configuration object specifying required roles and permissions.
|
|
172
|
+
* @param {JwtPayload} [jwtPayload] - The JWT payload containing additional authentication information.
|
|
173
|
+
* @throws {HttpError} If the user is not authorized to access url based on route config and JWT payload.
|
|
174
|
+
*/
|
|
175
|
+
authorize(authUser, url, routeConfig = {}, jwtPayload) {
|
|
176
|
+
if (!authUser ||
|
|
177
|
+
!authUser.enabled ||
|
|
178
|
+
(authUser.logoutAt &&
|
|
179
|
+
jwtPayload &&
|
|
180
|
+
new Date(authUser.logoutAt).getTime() > jwtPayload?.iat)) {
|
|
181
|
+
throw new errors_1.HttpError('Unauthorized access', 401);
|
|
182
|
+
}
|
|
183
|
+
if (jwtPayload && !(0, helper_1.checkScopeAccess)(url, jwtPayload.scope)) {
|
|
184
|
+
throw new errors_1.HttpError(`JWT token is not authorized to access requested URL`, 403);
|
|
185
|
+
}
|
|
186
|
+
const { roles, permissions } = routeConfig;
|
|
187
|
+
if (!(0, helper_1.hasRoles)(authUser, roles) || !(0, helper_1.hasPermissions)(authUser, permissions)) {
|
|
188
|
+
throw new errors_1.HttpError('Forbidden access', 403);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Authenticates a user based on the provided username and password and generate access tokens.
|
|
193
|
+
* If 2FA is forced at configuration level or auth user has enabled 2FA, then generate JWT with 2FA scope,
|
|
194
|
+
* otherwise return regular JWT with authentication scope.
|
|
195
|
+
*
|
|
196
|
+
* @param {string} username - The username of the user attempting to log in.
|
|
197
|
+
* @param {string} password - The password of the user attempting to log in.
|
|
198
|
+
* @return {Promise<AuthTokens>} A promise that resolves to an object containing authentication tokens if login is
|
|
199
|
+
* successful.
|
|
200
|
+
* @throws {HttpError} If the user does not exist, is disabled, or provides invalid credentials.
|
|
201
|
+
*/
|
|
202
|
+
async login(username, password) {
|
|
203
|
+
const authUser = await this.authenticate(username, password);
|
|
204
|
+
if (common_1.config.SECURITY_ACCOUNT_2FA_ENABLED &&
|
|
205
|
+
(common_1.config.SECURITY_ACCOUNT_2FA_FORCED || authUser.twoFactorAuth !== 'None')) {
|
|
206
|
+
return this.generateAuthTokens(authUser, common_1.AuthScope.TwoFA);
|
|
207
|
+
}
|
|
208
|
+
return this.generateAuthTokens(authUser);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Exchanges an existing token for new authentication tokens.
|
|
212
|
+
*
|
|
213
|
+
* @param {string} token - The token to be exchanged for new authentication tokens.
|
|
214
|
+
* @return {Promise<AuthTokens>} A promise that resolves to a set of new authentication tokens.
|
|
215
|
+
* @throws {HttpError} If the token is invalid, expired, or associated with a non-existent or disabled user.
|
|
216
|
+
*/
|
|
217
|
+
async exchangeToken(token) {
|
|
218
|
+
const { authUserId, authSource } = await this._securityStore.useOneTimeToken(token, common_1.AuthOTTPurpose.Authentication);
|
|
219
|
+
const authUser = await this.findById(authUserId);
|
|
220
|
+
if (!authUser || !authUser.enabled) {
|
|
221
|
+
throw new errors_1.HttpError('Auth user does not exist or is disabled', 400);
|
|
222
|
+
}
|
|
223
|
+
common_1.logger.debug({ id: authUser.id, token }, 'User token exchanged');
|
|
224
|
+
return this.generateAuthTokens(authUser, common_1.AuthScope.Auth, authSource);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Generates authentication tokens (access and refresh tokens) for a given authenticated user.
|
|
228
|
+
*
|
|
229
|
+
* @param {AuthUser} authUser - The authenticated user for whom the tokens are to be generated.
|
|
230
|
+
* Contains user identification and authentication details.
|
|
231
|
+
* @param {AuthScope} [scope=AuthScope.Auth] - The scope for which the tokens are generated.
|
|
232
|
+
* @param {AuthSource} [source=AuthSource.Password] - The authentication source used to generate access tokens.
|
|
233
|
+
* @return {Promise<AuthTokens>} A promise that resolves to an object containing the generated authentication tokens:
|
|
234
|
+
* - `accessToken` (string): The access token for API access.
|
|
235
|
+
* - `refreshToken` (string): The refresh token for acquiring new access tokens.
|
|
236
|
+
* - `expiresIn` (number): The expiration time (in seconds) of the access token.
|
|
237
|
+
* - `refreshExpiresIn` (number): The expiration time (in seconds) of the refresh token.
|
|
238
|
+
* @throws {HttpError} If the server instance is not initialized.
|
|
239
|
+
*/
|
|
240
|
+
async generateAuthTokens(authUser, scope = common_1.AuthScope.Auth, source = common_1.AuthSource.Password) {
|
|
241
|
+
const server = context_1.context.server;
|
|
242
|
+
if (!server) {
|
|
243
|
+
throw new errors_1.HttpError('Server instance not initialized');
|
|
244
|
+
}
|
|
245
|
+
const jwtPayload = {
|
|
246
|
+
scope,
|
|
247
|
+
source,
|
|
248
|
+
username: authUser.email,
|
|
249
|
+
sub: authUser.id,
|
|
250
|
+
iat: new Date().getTime()
|
|
251
|
+
};
|
|
252
|
+
const expiresIn = common_1.config.SECURITY_JWT_EXPIRES_IN;
|
|
253
|
+
const refreshExpiresIn = common_1.config.SECURITY_JWT_REFRESH_EXPIRES_IN;
|
|
254
|
+
const accessToken = server.jwt.sign(jwtPayload, { expiresIn });
|
|
255
|
+
const refreshToken = server.jwt.sign({ ...jwtPayload, scope: common_1.AuthScope.Refresh }, { expiresIn: refreshExpiresIn });
|
|
256
|
+
common_1.logger.debug({ id: authUser.id }, 'Access tokens generated');
|
|
257
|
+
return { accessToken, refreshToken, expiresIn, refreshExpiresIn };
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Logs out a user by updating their authentication information with a logout timestamp.
|
|
261
|
+
*
|
|
262
|
+
* @param {number} id - The unique identifier of the user to be logged out.
|
|
263
|
+
* @return {Promise<boolean>} A promise that resolves to a boolean indicating whether the logout operation was
|
|
264
|
+
* successful.
|
|
265
|
+
*/
|
|
266
|
+
async logout(id) {
|
|
267
|
+
await this._cacheService.removeCachedValue(`${AUTH_KEY}:${id}`);
|
|
268
|
+
common_1.logger.debug({ id }, 'User logout');
|
|
269
|
+
return !!(await this.updateAuthUser(id, { logoutAt: new Date() }));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
exports.AuthService = AuthService;
|
package/security/auth.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
40
|
+
const auth_1 = __importDefault(require("@fastify/auth"));
|
|
41
|
+
const common_1 = require("@appweaver/common");
|
|
42
|
+
const errors_1 = require("../errors");
|
|
43
|
+
const helper_1 = require("./helper");
|
|
44
|
+
const auth_routes_1 = require("./auth-routes");
|
|
45
|
+
const account_1 = require("./account");
|
|
46
|
+
const recaptcha_1 = require("./recaptcha");
|
|
47
|
+
const basic_1 = require("./basic");
|
|
48
|
+
const api_key_1 = require("./api-key");
|
|
49
|
+
const jwt_1 = require("./jwt");
|
|
50
|
+
const oauth2Plugins = __importStar(require("./oauth2"));
|
|
51
|
+
exports.default = (0, fastify_plugin_1.default)((server) => {
|
|
52
|
+
server.register(auth_1.default);
|
|
53
|
+
if (common_1.config.SECURITY_RECAPTCHA_ENABLED) {
|
|
54
|
+
server.register(recaptcha_1.recaptcha);
|
|
55
|
+
}
|
|
56
|
+
if (common_1.config.SECURITY_BASIC_ENABLED) {
|
|
57
|
+
server.register(basic_1.basicAuth);
|
|
58
|
+
}
|
|
59
|
+
if (common_1.config.SECURITY_API_KEY_ENABLED) {
|
|
60
|
+
server.register(api_key_1.apiKeyAuth);
|
|
61
|
+
}
|
|
62
|
+
server.register(jwt_1.jwtAuth);
|
|
63
|
+
// Load and register all plugins exported from oauth2 dir, plugin-enabled
|
|
64
|
+
// status and required config values are checked at plugin initialization
|
|
65
|
+
for (const oauth2Plugin of Object.values(oauth2Plugins)) {
|
|
66
|
+
server.register(oauth2Plugin);
|
|
67
|
+
}
|
|
68
|
+
server.register(auth_routes_1.authRoutes, { prefix: common_1.config.SECURITY_ROUTE_PREFIX });
|
|
69
|
+
server.register(account_1.accountRoutes, {
|
|
70
|
+
prefix: common_1.config.SECURITY_ACCOUNT_ROUTE_PREFIX
|
|
71
|
+
});
|
|
72
|
+
server.decorate('authenticate', (authTypes) => {
|
|
73
|
+
const { auth, authenticateJWT, authenticateApiKey, basicAuth } = server;
|
|
74
|
+
const authHandlers = [];
|
|
75
|
+
const optionalHandler = (hasAuthCredentials, handler) => async (req, reply) => {
|
|
76
|
+
if (hasAuthCredentials(req)) {
|
|
77
|
+
await handler(req, reply);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
// Register enabled authentication handlers. Each handler executes only when
|
|
81
|
+
// the request contains credentials matching its authentication flow; otherwise,
|
|
82
|
+
// it is skipped. At least one handler must succeed for the request to be
|
|
83
|
+
// authenticated; if all handlers are skipped, a 401 error is returned
|
|
84
|
+
for (const authType of authTypes ?? Object.values(common_1.AuthType)) {
|
|
85
|
+
switch (authType) {
|
|
86
|
+
case common_1.AuthType.Jwt:
|
|
87
|
+
authHandlers.push(optionalHandler(jwt_1.hasBearerAuth, authenticateJWT));
|
|
88
|
+
break;
|
|
89
|
+
case common_1.AuthType.ApiKey:
|
|
90
|
+
if (common_1.config.SECURITY_API_KEY_ENABLED) {
|
|
91
|
+
authHandlers.push(optionalHandler(api_key_1.hasApiKey, authenticateApiKey));
|
|
92
|
+
}
|
|
93
|
+
break;
|
|
94
|
+
case common_1.AuthType.Basic:
|
|
95
|
+
if (common_1.config.SECURITY_BASIC_ENABLED) {
|
|
96
|
+
authHandlers.push([
|
|
97
|
+
// Use OR condition since Basic auth plugin throws an error before
|
|
98
|
+
// the validation function is called. Also, the Basic auth plugin
|
|
99
|
+
// is using a callback-based handler
|
|
100
|
+
async (req) => {
|
|
101
|
+
if ((0, basic_1.hasBasicAuth)(req)) {
|
|
102
|
+
throw new errors_1.HttpError('Invalid authorization header', 401);
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
basicAuth
|
|
106
|
+
]);
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// If a user has not been authenticated using any of the provided methods,
|
|
112
|
+
// then throw 401 error
|
|
113
|
+
authHandlers.push(async () => {
|
|
114
|
+
if (!(0, helper_1.currentAuthUser)()) {
|
|
115
|
+
throw new errors_1.HttpError('Unauthorized', 401);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
return auth(authHandlers, { relation: 'and' });
|
|
119
|
+
});
|
|
120
|
+
server.decorate('currentUser', () => {
|
|
121
|
+
const authUser = (0, helper_1.currentAuthUser)();
|
|
122
|
+
if (!authUser) {
|
|
123
|
+
throw new errors_1.HttpError('Unauthorized', 401);
|
|
124
|
+
}
|
|
125
|
+
return authUser;
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.basicAuth = void 0;
|
|
7
|
+
exports.hasBasicAuth = hasBasicAuth;
|
|
8
|
+
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
9
|
+
const basic_auth_1 = __importDefault(require("@fastify/basic-auth"));
|
|
10
|
+
const request_context_1 = require("@fastify/request-context");
|
|
11
|
+
const common_1 = require("@appweaver/common");
|
|
12
|
+
const context_1 = require("../../context");
|
|
13
|
+
const auth_service_1 = require("../auth-service");
|
|
14
|
+
exports.basicAuth = (0, fastify_plugin_1.default)(async (server) => {
|
|
15
|
+
const authService = (0, context_1.inject)(auth_service_1.AuthService);
|
|
16
|
+
server.register(basic_auth_1.default, {
|
|
17
|
+
authenticate: common_1.config.SECURITY_BASIC_REALM
|
|
18
|
+
? { realm: common_1.config.SECURITY_BASIC_REALM }
|
|
19
|
+
: true,
|
|
20
|
+
proxyMode: common_1.config.SECURITY_BASIC_PROXY_MODE,
|
|
21
|
+
validate: async (username, password, request) => {
|
|
22
|
+
const authUser = await authService.authenticate(username, password);
|
|
23
|
+
authService.authorize(authUser, request.url, request.routeOptions.config);
|
|
24
|
+
request_context_1.requestContext.set('authUser', authUser);
|
|
25
|
+
request_context_1.requestContext.set('authType', common_1.AuthType.Basic);
|
|
26
|
+
request_context_1.requestContext.set('authSource', common_1.AuthSource.Password);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
function hasBasicAuth(request) {
|
|
31
|
+
return (request.headers.authorization?.toLowerCase().startsWith('basic') === true);
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './basic-auth';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./basic-auth"), exports);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Ctor, IResourceService, ResourceModel, ResourceModelConfig, ResourceServiceConfig } from '@appweaver/common';
|
|
2
|
+
import { RegistrationDataFn } from '../types';
|
|
3
|
+
export declare function createAuthModel(config: ResourceModelConfig): ResourceModel;
|
|
4
|
+
export declare function createAuthService<T = any, C = any, U = any>(config: ResourceServiceConfig<T, C, U> & {
|
|
5
|
+
registrationData?: RegistrationDataFn<T>;
|
|
6
|
+
}): Ctor<IResourceService<T, T, C, U>>;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAuthModel = createAuthModel;
|
|
4
|
+
exports.createAuthService = createAuthService;
|
|
5
|
+
const common_1 = require("@appweaver/common");
|
|
6
|
+
const helper_1 = require("./helper");
|
|
7
|
+
const factory_1 = require("../factory");
|
|
8
|
+
function createAuthModel(config) {
|
|
9
|
+
const authModelScalars = {
|
|
10
|
+
email: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
unique: true,
|
|
13
|
+
maxLength: 255,
|
|
14
|
+
format: 'email'
|
|
15
|
+
},
|
|
16
|
+
passwordHash: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
required: false,
|
|
19
|
+
hidden: true
|
|
20
|
+
},
|
|
21
|
+
verifiedEmail: {
|
|
22
|
+
type: 'boolean',
|
|
23
|
+
default: false
|
|
24
|
+
},
|
|
25
|
+
twoFactorAuth: {
|
|
26
|
+
type: 'enum',
|
|
27
|
+
values: ['None', 'Email'],
|
|
28
|
+
default: 'None'
|
|
29
|
+
},
|
|
30
|
+
enabled: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
default: true
|
|
33
|
+
},
|
|
34
|
+
logoutAt: {
|
|
35
|
+
type: 'dateTime',
|
|
36
|
+
required: false
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
// Override email properties if already present in the model config
|
|
40
|
+
if (config.scalars?.email?.type === 'string') {
|
|
41
|
+
authModelScalars.email = {
|
|
42
|
+
...authModelScalars.email,
|
|
43
|
+
...config.scalars.email
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const authModelVirtual = {
|
|
47
|
+
password: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
required: true,
|
|
50
|
+
input: {
|
|
51
|
+
type: 'all'
|
|
52
|
+
},
|
|
53
|
+
output: {
|
|
54
|
+
type: 'none'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const authModelRelations = {
|
|
59
|
+
roles: {
|
|
60
|
+
model: 'Role',
|
|
61
|
+
array: true,
|
|
62
|
+
input: {
|
|
63
|
+
type: 'all'
|
|
64
|
+
},
|
|
65
|
+
output: {
|
|
66
|
+
type: 'always',
|
|
67
|
+
include: {
|
|
68
|
+
permissions: {
|
|
69
|
+
type: 'always'
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
...(common_1.config.SECURITY_API_KEY_ENABLED
|
|
75
|
+
? {
|
|
76
|
+
apiKeys: {
|
|
77
|
+
model: 'ApiKey',
|
|
78
|
+
mappedBy: (0, common_1.uncapitalize)(config.name),
|
|
79
|
+
array: true,
|
|
80
|
+
input: {
|
|
81
|
+
type: 'none'
|
|
82
|
+
},
|
|
83
|
+
output: {
|
|
84
|
+
type: 'none'
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
: {})
|
|
89
|
+
};
|
|
90
|
+
const authModelInputOmit = ['verifiedEmail', 'logoutAt'];
|
|
91
|
+
config.scalars = { ...config.scalars, ...authModelScalars };
|
|
92
|
+
config.virtual = { ...config.virtual, ...authModelVirtual };
|
|
93
|
+
config.relations = { ...config.relations, ...authModelRelations };
|
|
94
|
+
config.create = {
|
|
95
|
+
...(config.create ?? {}),
|
|
96
|
+
omit: [...(config.create?.omit ?? []), ...authModelInputOmit]
|
|
97
|
+
};
|
|
98
|
+
config.update = {
|
|
99
|
+
...(config.update ?? {}),
|
|
100
|
+
omit: [...(config.update?.omit ?? []), ...authModelInputOmit]
|
|
101
|
+
};
|
|
102
|
+
const model = (0, factory_1.createModel)(config);
|
|
103
|
+
model[common_1.RESOURCE_AUTH] = true;
|
|
104
|
+
return model;
|
|
105
|
+
}
|
|
106
|
+
function createAuthService(config) {
|
|
107
|
+
// Capture original functions to invoke after new auth logic
|
|
108
|
+
const beforeCreate = config.beforeCreate;
|
|
109
|
+
const beforeUpdate = config.beforeUpdate;
|
|
110
|
+
config.beforeCreate = async (data) => {
|
|
111
|
+
await (0, helper_1.updatePasswordHash)(data, data['password']);
|
|
112
|
+
await beforeCreate?.(data);
|
|
113
|
+
};
|
|
114
|
+
config.beforeUpdate = async (id, data) => {
|
|
115
|
+
await (0, helper_1.updatePasswordHash)(data, data['password'], true);
|
|
116
|
+
await beforeUpdate?.(id, data);
|
|
117
|
+
};
|
|
118
|
+
if (!config.registrationData) {
|
|
119
|
+
config.registrationData = (_, email, password) => {
|
|
120
|
+
return {
|
|
121
|
+
email,
|
|
122
|
+
password
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const service = (0, factory_1.createService)(config);
|
|
127
|
+
service[common_1.RESOURCE_AUTH] = true;
|
|
128
|
+
return service;
|
|
129
|
+
}
|