@fonoster/identity 0.26.0 → 0.30.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/README.md +25 -0
- package/dist/allowList.d.ts +4 -0
- package/dist/allowList.js +7 -1
- package/dist/apikeys/createCreateApiKey.js +16 -0
- package/dist/apikeys/createDeleteApiKey.js +22 -2
- package/dist/apikeys/createListApiKeys.js +14 -0
- package/dist/apikeys/createRegenerateApiKey.js +23 -2
- package/dist/config/index.d.ts +20 -0
- package/dist/config/index.js +36 -0
- package/dist/config/publishedKeys.d.ts +9 -0
- package/dist/config/publishedKeys.js +56 -0
- package/dist/config/schemas.d.ts +392 -0
- package/dist/config/schemas.js +163 -0
- package/dist/config/toIdentityConfig.d.ts +19 -0
- package/dist/config/toIdentityConfig.js +93 -0
- package/dist/constants.d.ts +28 -0
- package/dist/constants.js +29 -1
- package/dist/exchanges/createExchangeApiKey.js +4 -1
- package/dist/exchanges/createExchangeCredentials.d.ts +6 -1
- package/dist/exchanges/createExchangeCredentials.js +20 -2
- package/dist/exchanges/createExchangeOauth2Code.js +25 -2
- package/dist/exchanges/createExchangeRefreshToken.d.ts +9 -1
- package/dist/exchanges/createExchangeRefreshToken.js +57 -13
- package/dist/exchanges/createExchangeVerificationCode.d.ts +35 -0
- package/dist/exchanges/createExchangeVerificationCode.js +95 -0
- package/dist/exchanges/createRevokeToken.d.ts +33 -0
- package/dist/exchanges/createRevokeToken.js +64 -0
- package/dist/exchanges/exchangeTokens.d.ts +3 -1
- package/dist/exchanges/exchangeTokens.js +23 -5
- package/dist/exchanges/index.d.ts +2 -0
- package/dist/exchanges/index.js +2 -0
- package/dist/exchanges/payloads/users/createGetIdTokenPayload.js +3 -2
- package/dist/exchanges/refreshTokens.d.ts +30 -0
- package/dist/exchanges/refreshTokens.js +101 -0
- package/dist/exchanges/types.d.ts +53 -2
- package/dist/generated/@prisma/client/edge.js +32 -9
- package/dist/generated/@prisma/client/index-browser.js +24 -1
- package/dist/generated/@prisma/client/index.d.ts +3233 -417
- package/dist/generated/@prisma/client/index.js +34 -11
- package/dist/generated/@prisma/client/{libquery_engine-darwin-arm64.dylib.node → libquery_engine-debian-openssl-3.0.x.so.node} +0 -0
- package/dist/generated/@prisma/client/package.json +1 -1
- package/dist/generated/@prisma/client/schema.prisma +47 -7
- package/dist/generated/@prisma/client/wasm.js +32 -9
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/server/config.d.ts +286 -55
- package/dist/server/config.js +74 -108
- package/dist/server/index.js +5 -3
- package/dist/service.d.ts +6 -0
- package/dist/service.js +11 -4
- package/dist/users/createCreateUser.d.ts +6 -1
- package/dist/users/createCreateUser.js +17 -9
- package/dist/users/createCreateUserWithOauth2Code.js +19 -15
- package/dist/users/createResetPassword.d.ts +5 -0
- package/dist/users/createResetPassword.js +33 -7
- package/dist/users/createSendResetPasswordCode.d.ts +6 -0
- package/dist/users/createSendResetPasswordCode.js +33 -1
- package/dist/users/createUpdateUser.d.ts +7 -0
- package/dist/users/createUpdateUser.js +41 -8
- package/dist/users/createUserFromGitHub.d.ts +40 -0
- package/dist/users/createUserFromGitHub.js +27 -0
- package/dist/users/index.d.ts +1 -0
- package/dist/users/index.js +1 -0
- package/dist/users/migratePasswordStorage.d.ts +7 -0
- package/dist/users/migratePasswordStorage.js +47 -0
- package/dist/users/types.d.ts +3 -1
- package/dist/users/upsertDefaultUser.js +6 -14
- package/dist/utils/authMethods.d.ts +31 -0
- package/dist/utils/authMethods.js +44 -0
- package/dist/utils/claimUnverifiedAccount.d.ts +35 -0
- package/dist/utils/claimUnverifiedAccount.js +43 -0
- package/dist/utils/clientAddress.d.ts +26 -0
- package/dist/utils/clientAddress.js +9 -0
- package/dist/utils/createGenerateVerificationCode.d.ts +4 -0
- package/dist/utils/createGenerateVerificationCode.js +12 -2
- package/dist/utils/createGenerateWorkspaceInviteToken.js +3 -0
- package/dist/utils/createGetApiKeyByAccessKeyId.d.ts +5 -1
- package/dist/utils/createGetApiKeyByAccessKeyId.js +5 -0
- package/dist/utils/createGetUserByAccessKeyId.d.ts +3 -1
- package/dist/utils/createGetUserByEmail.d.ts +3 -1
- package/dist/utils/createGetUserByGitHubEmails.d.ts +41 -0
- package/dist/utils/createGetUserByGitHubEmails.js +31 -0
- package/dist/utils/createGetUserByWorkspaceAccessKeyId.d.ts +3 -1
- package/dist/utils/createIsValidVerificationCode.d.ts +5 -0
- package/dist/utils/createIsValidVerificationCode.js +34 -7
- package/dist/utils/createSendEmail.d.ts +2 -1
- package/dist/utils/createSendEmail.js +17 -0
- package/dist/utils/createUpdateMembershipStatus.js +10 -3
- package/dist/utils/generateOneTimePassword.d.ts +3 -0
- package/dist/utils/generateOneTimePassword.js +25 -0
- package/dist/utils/getGitHubUserWithOauth2Code.d.ts +20 -2
- package/dist/utils/getGitHubUserWithOauth2Code.js +41 -9
- package/dist/utils/isWorkspaceAdminCall.d.ts +8 -0
- package/dist/utils/isWorkspaceAdminCall.js +47 -0
- package/dist/utils/normalizeContact.d.ts +22 -0
- package/dist/utils/normalizeContact.js +28 -0
- package/dist/utils/passwords.d.ts +11 -0
- package/dist/utils/passwords.js +84 -0
- package/dist/utils/rateLimiter.d.ts +20 -0
- package/dist/utils/rateLimiter.js +108 -0
- package/dist/utils/signInThrottle.d.ts +47 -0
- package/dist/utils/signInThrottle.js +90 -0
- package/dist/utils/userPasswords.d.ts +25 -0
- package/dist/utils/userPasswords.js +98 -0
- package/dist/verification/createSendVerificationCode.js +27 -1
- package/dist/verification/createVerifyCode.js +10 -0
- package/dist/workspaces/createCreateWorkspace.d.ts +7 -1
- package/dist/workspaces/createCreateWorkspace.js +41 -5
- package/dist/workspaces/createDeleteWorkspace.d.ts +2 -1
- package/dist/workspaces/createDeleteWorkspace.js +16 -2
- package/dist/workspaces/createInviteUserToWorkspace.js +19 -17
- package/dist/workspaces/createResendWorkspaceMembershipInvitation.js +17 -2
- package/dist/workspaces/createUpdateWorkspace.js +18 -4
- package/dist/workspaces/createWorkspaceDirectory.d.ts +29 -0
- package/dist/workspaces/createWorkspaceDirectory.js +42 -0
- package/dist/workspaces/index.d.ts +1 -0
- package/dist/workspaces/index.js +1 -0
- package/package.json +7 -6
- package/dist/generated/@prisma/client/runtime/wasm.js +0 -35
|
@@ -30,21 +30,51 @@ exports.createUpdateUser = createUpdateUser;
|
|
|
30
30
|
*/
|
|
31
31
|
const common_1 = require("@fonoster/common");
|
|
32
32
|
const logger_1 = require("@fonoster/logger");
|
|
33
|
+
const grpc_js_1 = require("@grpc/grpc-js");
|
|
34
|
+
const refreshTokens_1 = require("../exchanges/refreshTokens");
|
|
33
35
|
const utils_1 = require("../utils");
|
|
36
|
+
const userPasswords_1 = require("../utils/userPasswords");
|
|
34
37
|
const logger = (0, logger_1.getLogger)({ service: "identity", filePath: __filename });
|
|
38
|
+
/**
|
|
39
|
+
* Updates the caller's own profile. A new password needs the current one
|
|
40
|
+
* (an access token alone can't change it) and signs the user out everywhere:
|
|
41
|
+
* every refresh token is revoked. Accounts without a password (GitHub or
|
|
42
|
+
* email-code sign-in) set one with the password-reset flow, which proves the
|
|
43
|
+
* email.
|
|
44
|
+
*/
|
|
35
45
|
function createUpdateUser(prisma) {
|
|
46
|
+
const verifyUserPassword = (0, userPasswords_1.createVerifyUserPassword)(prisma);
|
|
47
|
+
const refreshTokens = (0, refreshTokens_1.createRefreshTokenStore)(prisma);
|
|
36
48
|
const updateUser = (call, callback) => __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
var _a;
|
|
37
50
|
const { request } = call;
|
|
38
|
-
const { ref, name, avatar, password, phone } = request;
|
|
51
|
+
const { ref, name, avatar, password, currentPassword, phone } = request;
|
|
39
52
|
const token = (0, common_1.getTokenFromCall)(call);
|
|
40
53
|
const accessKeyId = (0, utils_1.getAccessKeyIdFromToken)(token);
|
|
41
|
-
logger.verbose("call to updateUser", { ref
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
logger.verbose("call to updateUser", { ref });
|
|
55
|
+
if (password) {
|
|
56
|
+
const user = yield prisma.user.findFirst({
|
|
57
|
+
where: { ref, accessKeyId }
|
|
58
|
+
});
|
|
59
|
+
if (!user) {
|
|
60
|
+
return callback({ code: grpc_js_1.status.NOT_FOUND, message: "User not found" });
|
|
61
|
+
}
|
|
62
|
+
if (!user.passwordDigest && !user.password) {
|
|
63
|
+
return callback({
|
|
64
|
+
code: grpc_js_1.status.FAILED_PRECONDITION,
|
|
65
|
+
message: "This account has no password; set one with the password reset flow"
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (!(yield verifyUserPassword(user, (_a = currentPassword === null || currentPassword === void 0 ? void 0 : currentPassword.trim()) !== null && _a !== void 0 ? _a : ""))) {
|
|
69
|
+
return callback({
|
|
70
|
+
code: grpc_js_1.status.PERMISSION_DENIED,
|
|
71
|
+
message: "The current password is incorrect"
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const updateData = Object.assign(Object.assign({ name,
|
|
76
|
+
avatar }, (password
|
|
77
|
+
? Object.assign(Object.assign({}, (yield (0, userPasswords_1.passwordColumns)(password))), { mustChangePassword: false }) : {})), { updatedAt: new Date() });
|
|
48
78
|
if (phone && phone !== "") {
|
|
49
79
|
updateData.phoneNumber = phone;
|
|
50
80
|
updateData.phoneNumberVerified = false;
|
|
@@ -56,6 +86,9 @@ function createUpdateUser(prisma) {
|
|
|
56
86
|
},
|
|
57
87
|
data: updateData
|
|
58
88
|
});
|
|
89
|
+
if (password) {
|
|
90
|
+
yield refreshTokens.revokeAll(accessKeyId);
|
|
91
|
+
}
|
|
59
92
|
const response = {
|
|
60
93
|
ref
|
|
61
94
|
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { Prisma } from "../db";
|
|
20
|
+
import { GitHubUser } from "../utils/getGitHubUserWithOauth2Code";
|
|
21
|
+
/** Creates a user from a GitHub profile; GitHub users have no password. */
|
|
22
|
+
declare function createUserFromGitHub(prisma: Prisma): (githubUser: GitHubUser & {
|
|
23
|
+
email: string;
|
|
24
|
+
}) => Promise<{
|
|
25
|
+
name: string;
|
|
26
|
+
ref: string;
|
|
27
|
+
accessKeyId: string;
|
|
28
|
+
createdAt: Date;
|
|
29
|
+
updatedAt: Date;
|
|
30
|
+
email: string;
|
|
31
|
+
emailVerified: boolean;
|
|
32
|
+
password: string | null;
|
|
33
|
+
passwordDigest: string | null;
|
|
34
|
+
mustChangePassword: boolean;
|
|
35
|
+
phoneNumber: string | null;
|
|
36
|
+
phoneNumberVerified: boolean;
|
|
37
|
+
avatar: string | null;
|
|
38
|
+
extended: import("../generated/@prisma/client/runtime/library").JsonValue | null;
|
|
39
|
+
}>;
|
|
40
|
+
export { createUserFromGitHub };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createUserFromGitHub = createUserFromGitHub;
|
|
13
|
+
const generateAccessKeyId_1 = require("../utils/generateAccessKeyId");
|
|
14
|
+
/** Creates a user from a GitHub profile; GitHub users have no password. */
|
|
15
|
+
function createUserFromGitHub(prisma) {
|
|
16
|
+
return (githubUser) => __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
return prisma.user.create({
|
|
18
|
+
data: {
|
|
19
|
+
name: (githubUser.name || githubUser.email).slice(0, 60),
|
|
20
|
+
email: githubUser.email,
|
|
21
|
+
accessKeyId: (0, generateAccessKeyId_1.generateAccessKeyId)(generateAccessKeyId_1.AccessKeyIdType.USER),
|
|
22
|
+
emailVerified: true,
|
|
23
|
+
avatar: githubUser.avatar_url
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
package/dist/users/index.d.ts
CHANGED
package/dist/users/index.js
CHANGED
|
@@ -40,3 +40,4 @@ __exportStar(require("./createUpdateUser"), exports);
|
|
|
40
40
|
__exportStar(require("./upsertDefaultUser"), exports);
|
|
41
41
|
__exportStar(require("./createSendResetPasswordCode"), exports);
|
|
42
42
|
__exportStar(require("./createResetPassword"), exports);
|
|
43
|
+
__exportStar(require("./migratePasswordStorage"), exports);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IdentityConfig } from "../exchanges/types";
|
|
2
|
+
/**
|
|
3
|
+
* Boot-time step: moves passwords still stored with reversible encryption to
|
|
4
|
+
* one-way digests. Idempotent, so it runs on every start.
|
|
5
|
+
*/
|
|
6
|
+
declare function migratePasswordStorage(identityConfig: IdentityConfig): Promise<number>;
|
|
7
|
+
export { migratePasswordStorage };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.migratePasswordStorage = migratePasswordStorage;
|
|
13
|
+
/**
|
|
14
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
15
|
+
* http://github.com/fonoster/fonoster
|
|
16
|
+
*
|
|
17
|
+
* This file is part of Fonoster
|
|
18
|
+
*
|
|
19
|
+
* Licensed under the MIT License (the "License");
|
|
20
|
+
* you may not use this file except in compliance with
|
|
21
|
+
* the License. You may obtain a copy of the License at
|
|
22
|
+
*
|
|
23
|
+
* https://opensource.org/licenses/MIT
|
|
24
|
+
*
|
|
25
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
26
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
27
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
28
|
+
* See the License for the specific language governing permissions and
|
|
29
|
+
* limitations under the License.
|
|
30
|
+
*/
|
|
31
|
+
const db_1 = require("../db");
|
|
32
|
+
const userPasswords_1 = require("../utils/userPasswords");
|
|
33
|
+
/**
|
|
34
|
+
* Boot-time step: moves passwords still stored with reversible encryption to
|
|
35
|
+
* one-way digests. Idempotent, so it runs on every start.
|
|
36
|
+
*/
|
|
37
|
+
function migratePasswordStorage(identityConfig) {
|
|
38
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
39
|
+
const prisma = (0, db_1.createPrismaClient)(identityConfig.dbUrl, identityConfig.encryptionKey);
|
|
40
|
+
try {
|
|
41
|
+
return yield (0, userPasswords_1.backfillPasswordDigests)(prisma);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
yield prisma.$disconnect();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
package/dist/users/types.d.ts
CHANGED
|
@@ -24,7 +24,9 @@ type SendResetPasswordEmailRequest = {
|
|
|
24
24
|
type UserUpdateInput = {
|
|
25
25
|
name?: string;
|
|
26
26
|
avatar?: string;
|
|
27
|
-
password?: string;
|
|
27
|
+
password?: string | null;
|
|
28
|
+
passwordDigest?: string;
|
|
29
|
+
mustChangePassword?: boolean;
|
|
28
30
|
phoneNumber?: string;
|
|
29
31
|
phoneNumberVerified?: boolean;
|
|
30
32
|
};
|
|
@@ -32,6 +32,7 @@ const logger_1 = require("@fonoster/logger");
|
|
|
32
32
|
const zod_1 = require("zod");
|
|
33
33
|
const zod_validation_error_1 = require("zod-validation-error");
|
|
34
34
|
const db_1 = require("../db");
|
|
35
|
+
const userPasswords_1 = require("../utils/userPasswords");
|
|
35
36
|
const logger = (0, logger_1.getLogger)({ service: "identity", filePath: __filename });
|
|
36
37
|
const createUserRequestSchema = zod_1.z.object({
|
|
37
38
|
name: zod_1.z
|
|
@@ -51,26 +52,17 @@ function upsertDefaultUser(identityConfig, request) {
|
|
|
51
52
|
try {
|
|
52
53
|
const validatedRequest = createUserRequestSchema.parse(request);
|
|
53
54
|
const { name, email, password } = validatedRequest;
|
|
55
|
+
const passwordData = password ? yield (0, userPasswords_1.passwordColumns)(password) : {};
|
|
54
56
|
logger.verbose("call to upsertDefaultUser", {
|
|
55
57
|
email,
|
|
56
58
|
accessKeyId: USER_ACCESS_KEY_ID
|
|
57
59
|
});
|
|
58
60
|
yield prisma.user.upsert({
|
|
59
61
|
where: { ref: USER_REF },
|
|
60
|
-
update: {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
accessKeyId: USER_ACCESS_KEY_ID,
|
|
65
|
-
updatedAt: new Date()
|
|
66
|
-
},
|
|
67
|
-
create: {
|
|
68
|
-
ref: USER_REF,
|
|
69
|
-
name,
|
|
70
|
-
email,
|
|
71
|
-
password,
|
|
72
|
-
accessKeyId: USER_ACCESS_KEY_ID
|
|
73
|
-
}
|
|
62
|
+
update: Object.assign(Object.assign({ name,
|
|
63
|
+
email }, passwordData), { accessKeyId: USER_ACCESS_KEY_ID, updatedAt: new Date() }),
|
|
64
|
+
create: Object.assign(Object.assign({ ref: USER_REF, name,
|
|
65
|
+
email }, passwordData), { accessKeyId: USER_ACCESS_KEY_ID })
|
|
74
66
|
});
|
|
75
67
|
yield prisma.workspace.upsert({
|
|
76
68
|
where: { ref: WORKSPACE_REF },
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { status } from "@grpc/grpc-js";
|
|
20
|
+
import { IdentityConfig } from "../exchanges/types";
|
|
21
|
+
type AuthMethod = "password" | "email_code" | "github";
|
|
22
|
+
/** Whether a sign-in method is enabled (features.auth.methods). */
|
|
23
|
+
declare function isMethodEnabled(config: IdentityConfig, method: AuthMethod): boolean;
|
|
24
|
+
/** Whether people may create their own account (features.auth.signup: open). */
|
|
25
|
+
declare function isOpenSignup(config: IdentityConfig): boolean;
|
|
26
|
+
/** The error an exchange returns when its method is disabled. */
|
|
27
|
+
declare function methodDisabledError(method: AuthMethod): {
|
|
28
|
+
code: status;
|
|
29
|
+
message: string;
|
|
30
|
+
};
|
|
31
|
+
export { AuthMethod, isMethodEnabled, isOpenSignup, methodDisabledError };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isMethodEnabled = isMethodEnabled;
|
|
4
|
+
exports.isOpenSignup = isOpenSignup;
|
|
5
|
+
exports.methodDisabledError = methodDisabledError;
|
|
6
|
+
/**
|
|
7
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
8
|
+
* http://github.com/fonoster/fonoster
|
|
9
|
+
*
|
|
10
|
+
* This file is part of Fonoster
|
|
11
|
+
*
|
|
12
|
+
* Licensed under the MIT License (the "License");
|
|
13
|
+
* you may not use this file except in compliance with
|
|
14
|
+
* the License. You may obtain a copy of the License at
|
|
15
|
+
*
|
|
16
|
+
* https://opensource.org/licenses/MIT
|
|
17
|
+
*
|
|
18
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
+
* See the License for the specific language governing permissions and
|
|
22
|
+
* limitations under the License.
|
|
23
|
+
*/
|
|
24
|
+
const grpc_js_1 = require("@grpc/grpc-js");
|
|
25
|
+
/** Whether a sign-in method is enabled (features.auth.methods). */
|
|
26
|
+
function isMethodEnabled(config, method) {
|
|
27
|
+
return config.auth.methods.includes(method);
|
|
28
|
+
}
|
|
29
|
+
/** Whether people may create their own account (features.auth.signup: open). */
|
|
30
|
+
function isOpenSignup(config) {
|
|
31
|
+
return config.auth.signup === "open";
|
|
32
|
+
}
|
|
33
|
+
const METHOD_LABELS = {
|
|
34
|
+
password: "Password",
|
|
35
|
+
email_code: "Email code",
|
|
36
|
+
github: "GitHub"
|
|
37
|
+
};
|
|
38
|
+
/** The error an exchange returns when its method is disabled. */
|
|
39
|
+
function methodDisabledError(method) {
|
|
40
|
+
return {
|
|
41
|
+
code: grpc_js_1.status.PERMISSION_DENIED,
|
|
42
|
+
message: `${METHOD_LABELS[method]} sign-in is disabled`
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { Prisma } from "../db";
|
|
20
|
+
type ClaimableUser = {
|
|
21
|
+
ref: string;
|
|
22
|
+
accessKeyId: string;
|
|
23
|
+
emailVerified: boolean;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Called when someone proves they own an account's email (an emailed code, or
|
|
27
|
+
* a GitHub account with that verified email) and signs in with it. If the
|
|
28
|
+
* email had never been verified, whoever set the account's password may not
|
|
29
|
+
* own the address (they could have signed up with someone else's email before
|
|
30
|
+
* its owner did), so that password stops working and every session of the
|
|
31
|
+
* account ends. The email is marked verified. Already verified accounts are
|
|
32
|
+
* returned unchanged.
|
|
33
|
+
*/
|
|
34
|
+
declare function createClaimUnverifiedAccount(prisma: Prisma): <T extends ClaimableUser>(user: T) => Promise<T>;
|
|
35
|
+
export { createClaimUnverifiedAccount };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createClaimUnverifiedAccount = createClaimUnverifiedAccount;
|
|
13
|
+
const refreshTokens_1 = require("../exchanges/refreshTokens");
|
|
14
|
+
/**
|
|
15
|
+
* Called when someone proves they own an account's email (an emailed code, or
|
|
16
|
+
* a GitHub account with that verified email) and signs in with it. If the
|
|
17
|
+
* email had never been verified, whoever set the account's password may not
|
|
18
|
+
* own the address (they could have signed up with someone else's email before
|
|
19
|
+
* its owner did), so that password stops working and every session of the
|
|
20
|
+
* account ends. The email is marked verified. Already verified accounts are
|
|
21
|
+
* returned unchanged.
|
|
22
|
+
*/
|
|
23
|
+
function createClaimUnverifiedAccount(prisma) {
|
|
24
|
+
const refreshTokens = (0, refreshTokens_1.createRefreshTokenStore)(prisma);
|
|
25
|
+
return function claimUnverifiedAccount(user) {
|
|
26
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
if (user.emailVerified) {
|
|
28
|
+
return user;
|
|
29
|
+
}
|
|
30
|
+
const claimed = yield prisma.user.update({
|
|
31
|
+
where: { ref: user.ref },
|
|
32
|
+
data: {
|
|
33
|
+
emailVerified: true,
|
|
34
|
+
password: null,
|
|
35
|
+
passwordDigest: null,
|
|
36
|
+
mustChangePassword: false
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
yield refreshTokens.revokeAll(user.accessKeyId);
|
|
40
|
+
return claimed;
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { Metadata } from "@grpc/grpc-js";
|
|
20
|
+
type CallWithAddress = {
|
|
21
|
+
metadata?: Metadata;
|
|
22
|
+
getPeer?: () => string;
|
|
23
|
+
};
|
|
24
|
+
/** The caller's address: Envoy's x-forwarded-for when present, else the peer. */
|
|
25
|
+
declare function clientAddress(call: CallWithAddress): string;
|
|
26
|
+
export { CallWithAddress, clientAddress };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clientAddress = clientAddress;
|
|
4
|
+
/** The caller's address: Envoy's x-forwarded-for when present, else the peer. */
|
|
5
|
+
function clientAddress(call) {
|
|
6
|
+
var _a, _b, _c, _d;
|
|
7
|
+
const forwarded = (_c = (_b = (_a = call.metadata) === null || _a === void 0 ? void 0 : _a.get("x-forwarded-for")) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.toString();
|
|
8
|
+
return (forwarded === null || forwarded === void 0 ? void 0 : forwarded.split(",")[0].trim()) || ((_d = call.getPeer) === null || _d === void 0 ? void 0 : _d.call(call)) || "unknown";
|
|
9
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Prisma } from "../db";
|
|
2
2
|
import { VerificationType } from "../generated/@prisma/client";
|
|
3
|
+
/**
|
|
4
|
+
* Issues a 6-digit code for a contact. Earlier unused codes for the same
|
|
5
|
+
* contact stop working, so only the latest email or SMS is valid.
|
|
6
|
+
*/
|
|
3
7
|
declare function createGenerateVerificationCode(prisma: Prisma): (params: {
|
|
4
8
|
type: VerificationType;
|
|
5
9
|
value: string;
|
|
@@ -28,11 +28,21 @@ exports.createGenerateVerificationCode = createGenerateVerificationCode;
|
|
|
28
28
|
* See the License for the specific language governing permissions and
|
|
29
29
|
* limitations under the License.
|
|
30
30
|
*/
|
|
31
|
+
const crypto_1 = require("crypto");
|
|
31
32
|
const constants_1 = require("../constants");
|
|
33
|
+
const normalizeContact_1 = require("./normalizeContact");
|
|
34
|
+
/**
|
|
35
|
+
* Issues a 6-digit code for a contact. Earlier unused codes for the same
|
|
36
|
+
* contact stop working, so only the latest email or SMS is valid.
|
|
37
|
+
*/
|
|
32
38
|
function createGenerateVerificationCode(prisma) {
|
|
33
39
|
return (params) => __awaiter(this, void 0, void 0, function* () {
|
|
34
|
-
const { type
|
|
35
|
-
const
|
|
40
|
+
const { type } = params;
|
|
41
|
+
const value = (0, normalizeContact_1.normalizeContact)(type, params.value);
|
|
42
|
+
const code = (0, crypto_1.randomInt)(100000, 1000000).toString();
|
|
43
|
+
yield prisma.verificationCode.deleteMany({
|
|
44
|
+
where: { type, value, consumedAt: null }
|
|
45
|
+
});
|
|
36
46
|
yield prisma.verificationCode.create({
|
|
37
47
|
data: {
|
|
38
48
|
type,
|
|
@@ -31,6 +31,7 @@ exports.createGenerateWorkspaceInviteToken = createGenerateWorkspaceInviteToken;
|
|
|
31
31
|
* See the License for the specific language governing permissions and
|
|
32
32
|
* limitations under the License.
|
|
33
33
|
*/
|
|
34
|
+
const common_1 = require("@fonoster/common");
|
|
34
35
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
35
36
|
const constants_1 = require("../constants");
|
|
36
37
|
function createGenerateWorkspaceInviteToken(identityConfig) {
|
|
@@ -47,6 +48,8 @@ function createGenerateWorkspaceInviteToken(identityConfig) {
|
|
|
47
48
|
iss: issuer,
|
|
48
49
|
sub: userRef,
|
|
49
50
|
aud: audience,
|
|
51
|
+
// Its own tokenUse: never accepted as an access or refresh token.
|
|
52
|
+
tokenUse: common_1.TokenUseEnum.INVITE,
|
|
50
53
|
memberRef: memberRef,
|
|
51
54
|
accessKeyId
|
|
52
55
|
};
|
|
@@ -27,4 +27,8 @@ declare function createGetApiKeyByAccessKeyId(prisma: Prisma): (accessKeyId: str
|
|
|
27
27
|
accessKeySecret: string;
|
|
28
28
|
workspaceRef: string;
|
|
29
29
|
}>;
|
|
30
|
-
|
|
30
|
+
/** A key past its `expiresAt` no longer exchanges or refreshes. */
|
|
31
|
+
declare function isApiKeyExpired(key: {
|
|
32
|
+
expiresAt?: Date | null;
|
|
33
|
+
}, now?: Date): boolean;
|
|
34
|
+
export { createGetApiKeyByAccessKeyId, isApiKeyExpired };
|
|
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.createGetApiKeyByAccessKeyId = createGetApiKeyByAccessKeyId;
|
|
13
|
+
exports.isApiKeyExpired = isApiKeyExpired;
|
|
13
14
|
function createGetApiKeyByAccessKeyId(prisma) {
|
|
14
15
|
return function getApiKeyByAccessKeyId(accessKeyId) {
|
|
15
16
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -25,3 +26,7 @@ function createGetApiKeyByAccessKeyId(prisma) {
|
|
|
25
26
|
});
|
|
26
27
|
};
|
|
27
28
|
}
|
|
29
|
+
/** A key past its `expiresAt` no longer exchanges or refreshes. */
|
|
30
|
+
function isApiKeyExpired(key, now = new Date()) {
|
|
31
|
+
return key.expiresAt ? key.expiresAt.getTime() <= now.getTime() : false;
|
|
32
|
+
}
|
|
@@ -30,7 +30,9 @@ declare function createGetUserByAccessKeyId(prisma: Prisma): (accessKeyId: strin
|
|
|
30
30
|
updatedAt: Date;
|
|
31
31
|
email: string;
|
|
32
32
|
emailVerified: boolean;
|
|
33
|
-
password: string;
|
|
33
|
+
password: string | null;
|
|
34
|
+
passwordDigest: string | null;
|
|
35
|
+
mustChangePassword: boolean;
|
|
34
36
|
phoneNumber: string | null;
|
|
35
37
|
phoneNumberVerified: boolean;
|
|
36
38
|
avatar: string | null;
|
|
@@ -25,7 +25,9 @@ declare function createGetUserByEmail(prisma: Prisma): (email: string) => Promis
|
|
|
25
25
|
updatedAt: Date;
|
|
26
26
|
email: string;
|
|
27
27
|
emailVerified: boolean;
|
|
28
|
-
password: string;
|
|
28
|
+
password: string | null;
|
|
29
|
+
passwordDigest: string | null;
|
|
30
|
+
mustChangePassword: boolean;
|
|
29
31
|
phoneNumber: string | null;
|
|
30
32
|
phoneNumberVerified: boolean;
|
|
31
33
|
avatar: string | null;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
|
|
3
|
+
* http://github.com/fonoster/fonoster
|
|
4
|
+
*
|
|
5
|
+
* This file is part of Fonoster
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the MIT License (the "License");
|
|
8
|
+
* you may not use this file except in compliance with
|
|
9
|
+
* the License. You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* https://opensource.org/licenses/MIT
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import { Prisma } from "../db";
|
|
20
|
+
/**
|
|
21
|
+
* Finds the user for a GitHub account by any of its verified emails, primary
|
|
22
|
+
* first. Fonoster 0.27 matched GitHub's public profile email, which isn't
|
|
23
|
+
* always the primary one, so those users would otherwise get a second account.
|
|
24
|
+
*/
|
|
25
|
+
declare function createGetUserByGitHubEmails(prisma: Prisma): (emails: string[]) => Promise<{
|
|
26
|
+
name: string;
|
|
27
|
+
ref: string;
|
|
28
|
+
accessKeyId: string;
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
updatedAt: Date;
|
|
31
|
+
email: string;
|
|
32
|
+
emailVerified: boolean;
|
|
33
|
+
password: string | null;
|
|
34
|
+
passwordDigest: string | null;
|
|
35
|
+
mustChangePassword: boolean;
|
|
36
|
+
phoneNumber: string | null;
|
|
37
|
+
phoneNumberVerified: boolean;
|
|
38
|
+
avatar: string | null;
|
|
39
|
+
extended: import("../generated/@prisma/client/runtime/library").JsonValue | null;
|
|
40
|
+
}>;
|
|
41
|
+
export { createGetUserByGitHubEmails };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createGetUserByGitHubEmails = createGetUserByGitHubEmails;
|
|
13
|
+
const createGetUserByEmail_1 = require("./createGetUserByEmail");
|
|
14
|
+
/**
|
|
15
|
+
* Finds the user for a GitHub account by any of its verified emails, primary
|
|
16
|
+
* first. Fonoster 0.27 matched GitHub's public profile email, which isn't
|
|
17
|
+
* always the primary one, so those users would otherwise get a second account.
|
|
18
|
+
*/
|
|
19
|
+
function createGetUserByGitHubEmails(prisma) {
|
|
20
|
+
const getUserByEmail = (0, createGetUserByEmail_1.createGetUserByEmail)(prisma);
|
|
21
|
+
return function getUserByGitHubEmails(emails) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
for (const email of emails) {
|
|
24
|
+
const user = yield getUserByEmail(email);
|
|
25
|
+
if (user)
|
|
26
|
+
return user;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -25,7 +25,9 @@ declare function createGetUserByWorkspaceAccessKeyId(prisma: Prisma): (accessKey
|
|
|
25
25
|
updatedAt: Date;
|
|
26
26
|
email: string;
|
|
27
27
|
emailVerified: boolean;
|
|
28
|
-
password: string;
|
|
28
|
+
password: string | null;
|
|
29
|
+
passwordDigest: string | null;
|
|
30
|
+
mustChangePassword: boolean;
|
|
29
31
|
phoneNumber: string | null;
|
|
30
32
|
phoneNumberVerified: boolean;
|
|
31
33
|
avatar: string | null;
|