@spfn/auth 0.2.0-beta.1 → 0.2.0-beta.11
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 +169 -168
- package/dist/{dto-81uR9gzF.d.ts → authenticate-CU6_zQaa.d.ts} +184 -169
- package/dist/config.d.ts +4 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +146 -119
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/nextjs/api.js +1 -1
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs/server.js +0 -2
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +171 -403
- package/dist/server.js +217 -461
- package/dist/server.js.map +1 -1
- package/migrations/0000_premium_famine.sql +292 -0
- package/migrations/meta/0000_snapshot.json +1 -1
- package/migrations/meta/_journal.json +2 -2
- package/package.json +8 -11
- package/migrations/0000_mysterious_colossus.sql +0 -197
package/dist/server.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
-
}) : x)(function(x) {
|
|
6
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
-
});
|
|
9
3
|
var __esm = (fn, res) => function __init() {
|
|
10
4
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
11
5
|
};
|
|
@@ -6132,6 +6126,23 @@ var init_user_profiles_repository = __esm({
|
|
|
6132
6126
|
const result = await this.db.delete(userProfiles).where(eq8(userProfiles.userId, userId)).returning();
|
|
6133
6127
|
return result[0] ?? null;
|
|
6134
6128
|
}
|
|
6129
|
+
/**
|
|
6130
|
+
* 프로필 Upsert (by User ID)
|
|
6131
|
+
*
|
|
6132
|
+
* 프로필이 없으면 생성, 있으면 업데이트
|
|
6133
|
+
* 새로 생성 시 displayName은 필수 (없으면 'User'로 설정)
|
|
6134
|
+
*/
|
|
6135
|
+
async upsertByUserId(userId, data) {
|
|
6136
|
+
const existing = await this.findByUserId(userId);
|
|
6137
|
+
if (existing) {
|
|
6138
|
+
return await this.updateByUserId(userId, data);
|
|
6139
|
+
}
|
|
6140
|
+
return await this.create({
|
|
6141
|
+
userId,
|
|
6142
|
+
displayName: data.displayName || "User",
|
|
6143
|
+
...data
|
|
6144
|
+
});
|
|
6145
|
+
}
|
|
6135
6146
|
/**
|
|
6136
6147
|
* User ID로 프로필 데이터 조회 (formatted)
|
|
6137
6148
|
*
|
|
@@ -6151,6 +6162,7 @@ var init_user_profiles_repository = __esm({
|
|
|
6151
6162
|
location: userProfiles.location,
|
|
6152
6163
|
company: userProfiles.company,
|
|
6153
6164
|
jobTitle: userProfiles.jobTitle,
|
|
6165
|
+
metadata: userProfiles.metadata,
|
|
6154
6166
|
createdAt: userProfiles.createdAt,
|
|
6155
6167
|
updatedAt: userProfiles.updatedAt
|
|
6156
6168
|
}).from(userProfiles).where(eq8(userProfiles.userId, userId)).limit(1).then((rows) => rows[0] ?? null);
|
|
@@ -6170,6 +6182,7 @@ var init_user_profiles_repository = __esm({
|
|
|
6170
6182
|
location: profile.location,
|
|
6171
6183
|
company: profile.company,
|
|
6172
6184
|
jobTitle: profile.jobTitle,
|
|
6185
|
+
metadata: profile.metadata,
|
|
6173
6186
|
createdAt: profile.createdAt,
|
|
6174
6187
|
updatedAt: profile.updatedAt
|
|
6175
6188
|
};
|
|
@@ -6682,9 +6695,10 @@ import {
|
|
|
6682
6695
|
} from "@spfn/auth/errors";
|
|
6683
6696
|
|
|
6684
6697
|
// src/server/services/verification.service.ts
|
|
6685
|
-
import { env as
|
|
6698
|
+
import { env as env3 } from "@spfn/auth/config";
|
|
6686
6699
|
import { InvalidVerificationCodeError } from "@spfn/auth/errors";
|
|
6687
6700
|
import jwt2 from "jsonwebtoken";
|
|
6701
|
+
import { sendEmail, sendSMS } from "@spfn/notification/server";
|
|
6688
6702
|
|
|
6689
6703
|
// src/server/logger.ts
|
|
6690
6704
|
import { logger as rootLogger } from "@spfn/core/logger";
|
|
@@ -6704,410 +6718,6 @@ var authLogger = {
|
|
|
6704
6718
|
|
|
6705
6719
|
// src/server/services/verification.service.ts
|
|
6706
6720
|
init_repositories();
|
|
6707
|
-
|
|
6708
|
-
// src/server/services/sms/provider.ts
|
|
6709
|
-
var currentProvider = null;
|
|
6710
|
-
var fallbackProvider = {
|
|
6711
|
-
name: "fallback",
|
|
6712
|
-
sendSMS: async (params) => {
|
|
6713
|
-
authLogger.sms.debug("DEV MODE - SMS not actually sent", {
|
|
6714
|
-
phone: params.phone,
|
|
6715
|
-
message: params.message,
|
|
6716
|
-
purpose: params.purpose || "N/A"
|
|
6717
|
-
});
|
|
6718
|
-
return {
|
|
6719
|
-
success: true,
|
|
6720
|
-
messageId: "dev-mode-no-actual-sms"
|
|
6721
|
-
};
|
|
6722
|
-
}
|
|
6723
|
-
};
|
|
6724
|
-
function registerSMSProvider(provider) {
|
|
6725
|
-
currentProvider = provider;
|
|
6726
|
-
authLogger.sms.info("Registered SMS provider", { name: provider.name });
|
|
6727
|
-
}
|
|
6728
|
-
function getSMSProvider() {
|
|
6729
|
-
return currentProvider || fallbackProvider;
|
|
6730
|
-
}
|
|
6731
|
-
async function sendSMS(params) {
|
|
6732
|
-
const provider = getSMSProvider();
|
|
6733
|
-
return await provider.sendSMS(params);
|
|
6734
|
-
}
|
|
6735
|
-
|
|
6736
|
-
// src/server/services/sms/aws-sns.provider.ts
|
|
6737
|
-
import { env as env3 } from "@spfn/auth/config";
|
|
6738
|
-
function isValidE164Phone(phone) {
|
|
6739
|
-
const e164Regex = /^\+[1-9]\d{1,14}$/;
|
|
6740
|
-
return e164Regex.test(phone);
|
|
6741
|
-
}
|
|
6742
|
-
function createAWSSNSProvider() {
|
|
6743
|
-
try {
|
|
6744
|
-
const { SNSClient, PublishCommand } = __require("@aws-sdk/client-sns");
|
|
6745
|
-
return {
|
|
6746
|
-
name: "aws-sns",
|
|
6747
|
-
sendSMS: async (params) => {
|
|
6748
|
-
const { phone, message, purpose } = params;
|
|
6749
|
-
if (!isValidE164Phone(phone)) {
|
|
6750
|
-
return {
|
|
6751
|
-
success: false,
|
|
6752
|
-
error: "Invalid phone number format. Must be E.164 format (e.g., +821012345678)"
|
|
6753
|
-
};
|
|
6754
|
-
}
|
|
6755
|
-
if (!env3.SPFN_AUTH_AWS_SNS_ACCESS_KEY_ID) {
|
|
6756
|
-
return {
|
|
6757
|
-
success: false,
|
|
6758
|
-
error: "AWS SNS credentials not configured. Set SPFN_AUTH_AWS_SNS_ACCESS_KEY_ID environment variable."
|
|
6759
|
-
};
|
|
6760
|
-
}
|
|
6761
|
-
try {
|
|
6762
|
-
const config = {
|
|
6763
|
-
region: env3.SPFN_AUTH_AWS_REGION || "ap-northeast-2"
|
|
6764
|
-
};
|
|
6765
|
-
if (env3.SPFN_AUTH_AWS_SNS_ACCESS_KEY_ID && env3.SPFN_AUTH_AWS_SNS_SECRET_ACCESS_KEY) {
|
|
6766
|
-
config.credentials = {
|
|
6767
|
-
accessKeyId: env3.SPFN_AUTH_AWS_SNS_ACCESS_KEY_ID,
|
|
6768
|
-
secretAccessKey: env3.SPFN_AUTH_AWS_SNS_SECRET_ACCESS_KEY
|
|
6769
|
-
};
|
|
6770
|
-
}
|
|
6771
|
-
const client = new SNSClient(config);
|
|
6772
|
-
const command = new PublishCommand({
|
|
6773
|
-
PhoneNumber: phone,
|
|
6774
|
-
Message: message,
|
|
6775
|
-
MessageAttributes: {
|
|
6776
|
-
"AWS.SNS.SMS.SMSType": {
|
|
6777
|
-
DataType: "String",
|
|
6778
|
-
StringValue: "Transactional"
|
|
6779
|
-
// For OTP codes
|
|
6780
|
-
},
|
|
6781
|
-
...env3.SPFN_AUTH_AWS_SNS_SENDER_ID && {
|
|
6782
|
-
"AWS.SNS.SMS.SenderID": {
|
|
6783
|
-
DataType: "String",
|
|
6784
|
-
StringValue: env3.SPFN_AUTH_AWS_SNS_SENDER_ID
|
|
6785
|
-
}
|
|
6786
|
-
}
|
|
6787
|
-
}
|
|
6788
|
-
});
|
|
6789
|
-
const response = await client.send(command);
|
|
6790
|
-
authLogger.sms.info("SMS sent via AWS SNS", {
|
|
6791
|
-
phone,
|
|
6792
|
-
messageId: response.MessageId,
|
|
6793
|
-
purpose: purpose || "N/A"
|
|
6794
|
-
});
|
|
6795
|
-
return {
|
|
6796
|
-
success: true,
|
|
6797
|
-
messageId: response.MessageId
|
|
6798
|
-
};
|
|
6799
|
-
} catch (error) {
|
|
6800
|
-
const err = error;
|
|
6801
|
-
authLogger.sms.error("Failed to send SMS via AWS SNS", {
|
|
6802
|
-
phone,
|
|
6803
|
-
error: err.message
|
|
6804
|
-
});
|
|
6805
|
-
return {
|
|
6806
|
-
success: false,
|
|
6807
|
-
error: err.message || "Failed to send SMS via AWS SNS"
|
|
6808
|
-
};
|
|
6809
|
-
}
|
|
6810
|
-
}
|
|
6811
|
-
};
|
|
6812
|
-
} catch (error) {
|
|
6813
|
-
return null;
|
|
6814
|
-
}
|
|
6815
|
-
}
|
|
6816
|
-
var awsSNSProvider = createAWSSNSProvider();
|
|
6817
|
-
|
|
6818
|
-
// src/server/services/sms/index.ts
|
|
6819
|
-
if (awsSNSProvider) {
|
|
6820
|
-
registerSMSProvider(awsSNSProvider);
|
|
6821
|
-
}
|
|
6822
|
-
|
|
6823
|
-
// src/server/services/email/provider.ts
|
|
6824
|
-
var currentProvider2 = null;
|
|
6825
|
-
var fallbackProvider2 = {
|
|
6826
|
-
name: "fallback",
|
|
6827
|
-
sendEmail: async (params) => {
|
|
6828
|
-
authLogger.email.debug("DEV MODE - Email not actually sent", {
|
|
6829
|
-
to: params.to,
|
|
6830
|
-
subject: params.subject,
|
|
6831
|
-
purpose: params.purpose || "N/A",
|
|
6832
|
-
textPreview: params.text?.substring(0, 100) || "N/A"
|
|
6833
|
-
});
|
|
6834
|
-
return {
|
|
6835
|
-
success: true,
|
|
6836
|
-
messageId: "dev-mode-no-actual-email"
|
|
6837
|
-
};
|
|
6838
|
-
}
|
|
6839
|
-
};
|
|
6840
|
-
function registerEmailProvider(provider) {
|
|
6841
|
-
currentProvider2 = provider;
|
|
6842
|
-
authLogger.email.info("Registered email provider", { name: provider.name });
|
|
6843
|
-
}
|
|
6844
|
-
function getEmailProvider() {
|
|
6845
|
-
return currentProvider2 || fallbackProvider2;
|
|
6846
|
-
}
|
|
6847
|
-
async function sendEmail(params) {
|
|
6848
|
-
const provider = getEmailProvider();
|
|
6849
|
-
return await provider.sendEmail(params);
|
|
6850
|
-
}
|
|
6851
|
-
|
|
6852
|
-
// src/server/services/email/aws-ses.provider.ts
|
|
6853
|
-
import { env as env4 } from "@spfn/auth/config";
|
|
6854
|
-
function isValidEmail(email) {
|
|
6855
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6856
|
-
return emailRegex.test(email);
|
|
6857
|
-
}
|
|
6858
|
-
function createAWSSESProvider() {
|
|
6859
|
-
try {
|
|
6860
|
-
const { SESClient, SendEmailCommand } = __require("@aws-sdk/client-ses");
|
|
6861
|
-
return {
|
|
6862
|
-
name: "aws-ses",
|
|
6863
|
-
sendEmail: async (params) => {
|
|
6864
|
-
const { to, subject, text: text10, html, purpose } = params;
|
|
6865
|
-
if (!isValidEmail(to)) {
|
|
6866
|
-
return {
|
|
6867
|
-
success: false,
|
|
6868
|
-
error: "Invalid email address format"
|
|
6869
|
-
};
|
|
6870
|
-
}
|
|
6871
|
-
if (!env4.SPFN_AUTH_AWS_SES_ACCESS_KEY_ID) {
|
|
6872
|
-
return {
|
|
6873
|
-
success: false,
|
|
6874
|
-
error: "AWS SES credentials not configured. Set SPFN_AUTH_AWS_SES_ACCESS_KEY_ID environment variable."
|
|
6875
|
-
};
|
|
6876
|
-
}
|
|
6877
|
-
if (!env4.SPFN_AUTH_AWS_SES_FROM_EMAIL) {
|
|
6878
|
-
return {
|
|
6879
|
-
success: false,
|
|
6880
|
-
error: "AWS SES sender email not configured. Set SPFN_AUTH_AWS_SES_FROM_EMAIL environment variable."
|
|
6881
|
-
};
|
|
6882
|
-
}
|
|
6883
|
-
try {
|
|
6884
|
-
const config = {
|
|
6885
|
-
region: env4.SPFN_AUTH_AWS_REGION || "ap-northeast-2"
|
|
6886
|
-
};
|
|
6887
|
-
if (env4.SPFN_AUTH_AWS_SES_ACCESS_KEY_ID && env4.SPFN_AUTH_AWS_SES_SECRET_ACCESS_KEY) {
|
|
6888
|
-
config.credentials = {
|
|
6889
|
-
accessKeyId: env4.SPFN_AUTH_AWS_SES_ACCESS_KEY_ID,
|
|
6890
|
-
secretAccessKey: env4.SPFN_AUTH_AWS_SES_SECRET_ACCESS_KEY
|
|
6891
|
-
};
|
|
6892
|
-
}
|
|
6893
|
-
const client = new SESClient(config);
|
|
6894
|
-
const body = {};
|
|
6895
|
-
if (text10) {
|
|
6896
|
-
body.Text = {
|
|
6897
|
-
Charset: "UTF-8",
|
|
6898
|
-
Data: text10
|
|
6899
|
-
};
|
|
6900
|
-
}
|
|
6901
|
-
if (html) {
|
|
6902
|
-
body.Html = {
|
|
6903
|
-
Charset: "UTF-8",
|
|
6904
|
-
Data: html
|
|
6905
|
-
};
|
|
6906
|
-
}
|
|
6907
|
-
const command = new SendEmailCommand({
|
|
6908
|
-
Source: env4.SPFN_AUTH_AWS_SES_FROM_EMAIL,
|
|
6909
|
-
Destination: {
|
|
6910
|
-
ToAddresses: [to]
|
|
6911
|
-
},
|
|
6912
|
-
Message: {
|
|
6913
|
-
Subject: {
|
|
6914
|
-
Charset: "UTF-8",
|
|
6915
|
-
Data: subject
|
|
6916
|
-
},
|
|
6917
|
-
Body: body
|
|
6918
|
-
}
|
|
6919
|
-
});
|
|
6920
|
-
const response = await client.send(command);
|
|
6921
|
-
authLogger.email.info("Email sent via AWS SES", {
|
|
6922
|
-
to,
|
|
6923
|
-
messageId: response.MessageId,
|
|
6924
|
-
purpose: purpose || "N/A"
|
|
6925
|
-
});
|
|
6926
|
-
return {
|
|
6927
|
-
success: true,
|
|
6928
|
-
messageId: response.MessageId
|
|
6929
|
-
};
|
|
6930
|
-
} catch (error) {
|
|
6931
|
-
const err = error;
|
|
6932
|
-
authLogger.email.error("Failed to send email via AWS SES", {
|
|
6933
|
-
to,
|
|
6934
|
-
error: err.message
|
|
6935
|
-
});
|
|
6936
|
-
return {
|
|
6937
|
-
success: false,
|
|
6938
|
-
error: err.message || "Failed to send email via AWS SES"
|
|
6939
|
-
};
|
|
6940
|
-
}
|
|
6941
|
-
}
|
|
6942
|
-
};
|
|
6943
|
-
} catch (error) {
|
|
6944
|
-
return null;
|
|
6945
|
-
}
|
|
6946
|
-
}
|
|
6947
|
-
var awsSESProvider = createAWSSESProvider();
|
|
6948
|
-
|
|
6949
|
-
// src/server/services/email/index.ts
|
|
6950
|
-
if (awsSESProvider) {
|
|
6951
|
-
registerEmailProvider(awsSESProvider);
|
|
6952
|
-
}
|
|
6953
|
-
|
|
6954
|
-
// src/server/services/email/templates/verification-code.ts
|
|
6955
|
-
function getSubject(purpose) {
|
|
6956
|
-
switch (purpose) {
|
|
6957
|
-
case "registration":
|
|
6958
|
-
return "Verify your email address";
|
|
6959
|
-
case "login":
|
|
6960
|
-
return "Your login verification code";
|
|
6961
|
-
case "password_reset":
|
|
6962
|
-
return "Reset your password";
|
|
6963
|
-
default:
|
|
6964
|
-
return "Your verification code";
|
|
6965
|
-
}
|
|
6966
|
-
}
|
|
6967
|
-
function getPurposeText(purpose) {
|
|
6968
|
-
switch (purpose) {
|
|
6969
|
-
case "registration":
|
|
6970
|
-
return "complete your registration";
|
|
6971
|
-
case "login":
|
|
6972
|
-
return "verify your identity";
|
|
6973
|
-
case "password_reset":
|
|
6974
|
-
return "reset your password";
|
|
6975
|
-
default:
|
|
6976
|
-
return "verify your identity";
|
|
6977
|
-
}
|
|
6978
|
-
}
|
|
6979
|
-
function generateText(params) {
|
|
6980
|
-
const { code, expiresInMinutes = 5 } = params;
|
|
6981
|
-
return `Your verification code is: ${code}
|
|
6982
|
-
|
|
6983
|
-
This code will expire in ${expiresInMinutes} minutes.
|
|
6984
|
-
|
|
6985
|
-
If you didn't request this code, please ignore this email.`;
|
|
6986
|
-
}
|
|
6987
|
-
function generateHTML(params) {
|
|
6988
|
-
const { code, purpose, expiresInMinutes = 5, appName } = params;
|
|
6989
|
-
const purposeText = getPurposeText(purpose);
|
|
6990
|
-
return `<!DOCTYPE html>
|
|
6991
|
-
<html>
|
|
6992
|
-
<head>
|
|
6993
|
-
<meta charset="utf-8">
|
|
6994
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6995
|
-
<title>Verification Code</title>
|
|
6996
|
-
</head>
|
|
6997
|
-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px; background-color: #f5f5f5;">
|
|
6998
|
-
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; border-radius: 10px 10px 0 0; text-align: center;">
|
|
6999
|
-
<h1 style="color: white; margin: 0; font-size: 24px;">${appName ? appName : "Verification Code"}</h1>
|
|
7000
|
-
</div>
|
|
7001
|
-
<div style="background: #ffffff; padding: 30px; border: 1px solid #e0e0e0; border-top: none; border-radius: 0 0 10px 10px;">
|
|
7002
|
-
<p style="margin-bottom: 20px; font-size: 16px;">
|
|
7003
|
-
Please use the following verification code to ${purposeText}:
|
|
7004
|
-
</p>
|
|
7005
|
-
<div style="background: #f8f9fa; padding: 25px; border-radius: 8px; text-align: center; margin: 25px 0; border: 2px dashed #dee2e6;">
|
|
7006
|
-
<span style="font-size: 36px; font-weight: bold; letter-spacing: 10px; color: #333; font-family: 'Courier New', monospace;">${code}</span>
|
|
7007
|
-
</div>
|
|
7008
|
-
<p style="color: #666; font-size: 14px; margin-top: 20px; text-align: center;">
|
|
7009
|
-
<strong>This code will expire in ${expiresInMinutes} minutes.</strong>
|
|
7010
|
-
</p>
|
|
7011
|
-
<hr style="border: none; border-top: 1px solid #eee; margin: 30px 0;">
|
|
7012
|
-
<p style="color: #999; font-size: 12px; text-align: center; margin: 0;">
|
|
7013
|
-
If you didn't request this code, please ignore this email.
|
|
7014
|
-
</p>
|
|
7015
|
-
</div>
|
|
7016
|
-
<div style="text-align: center; padding: 20px; color: #999; font-size: 11px;">
|
|
7017
|
-
<p style="margin: 0;">This is an automated message. Please do not reply.</p>
|
|
7018
|
-
</div>
|
|
7019
|
-
</body>
|
|
7020
|
-
</html>`;
|
|
7021
|
-
}
|
|
7022
|
-
function verificationCodeTemplate(params) {
|
|
7023
|
-
return {
|
|
7024
|
-
subject: getSubject(params.purpose),
|
|
7025
|
-
text: generateText(params),
|
|
7026
|
-
html: generateHTML(params)
|
|
7027
|
-
};
|
|
7028
|
-
}
|
|
7029
|
-
|
|
7030
|
-
// src/server/services/email/templates/registry.ts
|
|
7031
|
-
var customTemplates = {};
|
|
7032
|
-
function registerEmailTemplates(templates) {
|
|
7033
|
-
customTemplates = { ...customTemplates, ...templates };
|
|
7034
|
-
authLogger.email.info("Registered custom email templates", {
|
|
7035
|
-
templates: Object.keys(templates)
|
|
7036
|
-
});
|
|
7037
|
-
}
|
|
7038
|
-
function getVerificationCodeTemplate(params) {
|
|
7039
|
-
if (customTemplates.verificationCode) {
|
|
7040
|
-
return customTemplates.verificationCode(params);
|
|
7041
|
-
}
|
|
7042
|
-
return verificationCodeTemplate(params);
|
|
7043
|
-
}
|
|
7044
|
-
function getWelcomeTemplate(params) {
|
|
7045
|
-
if (customTemplates.welcome) {
|
|
7046
|
-
return customTemplates.welcome(params);
|
|
7047
|
-
}
|
|
7048
|
-
return {
|
|
7049
|
-
subject: params.appName ? `Welcome to ${params.appName}!` : "Welcome!",
|
|
7050
|
-
text: `Welcome! Your account has been created successfully.`,
|
|
7051
|
-
html: `
|
|
7052
|
-
<!DOCTYPE html>
|
|
7053
|
-
<html>
|
|
7054
|
-
<head><meta charset="utf-8"></head>
|
|
7055
|
-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px;">
|
|
7056
|
-
<h1>Welcome${params.appName ? ` to ${params.appName}` : ""}!</h1>
|
|
7057
|
-
<p>Your account has been created successfully.</p>
|
|
7058
|
-
</body>
|
|
7059
|
-
</html>`
|
|
7060
|
-
};
|
|
7061
|
-
}
|
|
7062
|
-
function getPasswordResetTemplate(params) {
|
|
7063
|
-
if (customTemplates.passwordReset) {
|
|
7064
|
-
return customTemplates.passwordReset(params);
|
|
7065
|
-
}
|
|
7066
|
-
const expires = params.expiresInMinutes || 30;
|
|
7067
|
-
return {
|
|
7068
|
-
subject: "Reset your password",
|
|
7069
|
-
text: `Click this link to reset your password: ${params.resetLink}
|
|
7070
|
-
|
|
7071
|
-
This link will expire in ${expires} minutes.`,
|
|
7072
|
-
html: `
|
|
7073
|
-
<!DOCTYPE html>
|
|
7074
|
-
<html>
|
|
7075
|
-
<head><meta charset="utf-8"></head>
|
|
7076
|
-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px;">
|
|
7077
|
-
<h1>Reset Your Password</h1>
|
|
7078
|
-
<p>Click the button below to reset your password:</p>
|
|
7079
|
-
<a href="${params.resetLink}" style="display: inline-block; background: #667eea; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px;">Reset Password</a>
|
|
7080
|
-
<p style="color: #666; margin-top: 20px;">This link will expire in ${expires} minutes.</p>
|
|
7081
|
-
</body>
|
|
7082
|
-
</html>`
|
|
7083
|
-
};
|
|
7084
|
-
}
|
|
7085
|
-
function getInvitationTemplate(params) {
|
|
7086
|
-
if (customTemplates.invitation) {
|
|
7087
|
-
return customTemplates.invitation(params);
|
|
7088
|
-
}
|
|
7089
|
-
const appName = params.appName || "our platform";
|
|
7090
|
-
const inviterText = params.inviterName ? `${params.inviterName} has invited you` : "You have been invited";
|
|
7091
|
-
const roleText = params.roleName ? ` as ${params.roleName}` : "";
|
|
7092
|
-
return {
|
|
7093
|
-
subject: `You're invited to join ${appName}`,
|
|
7094
|
-
text: `${inviterText} to join ${appName}${roleText}.
|
|
7095
|
-
|
|
7096
|
-
Click here to accept: ${params.inviteLink}`,
|
|
7097
|
-
html: `
|
|
7098
|
-
<!DOCTYPE html>
|
|
7099
|
-
<html>
|
|
7100
|
-
<head><meta charset="utf-8"></head>
|
|
7101
|
-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px;">
|
|
7102
|
-
<h1>You're Invited!</h1>
|
|
7103
|
-
<p>${inviterText} to join <strong>${appName}</strong>${roleText}.</p>
|
|
7104
|
-
<a href="${params.inviteLink}" style="display: inline-block; background: #667eea; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px;">Accept Invitation</a>
|
|
7105
|
-
</body>
|
|
7106
|
-
</html>`
|
|
7107
|
-
};
|
|
7108
|
-
}
|
|
7109
|
-
|
|
7110
|
-
// src/server/services/verification.service.ts
|
|
7111
6721
|
var VERIFICATION_TOKEN_EXPIRY = "15m";
|
|
7112
6722
|
var VERIFICATION_CODE_EXPIRY_MINUTES = 5;
|
|
7113
6723
|
var MAX_VERIFICATION_ATTEMPTS = 5;
|
|
@@ -7151,7 +6761,7 @@ async function markCodeAsUsed(codeId) {
|
|
|
7151
6761
|
await verificationCodesRepository.markAsUsed(codeId);
|
|
7152
6762
|
}
|
|
7153
6763
|
function createVerificationToken(payload) {
|
|
7154
|
-
return jwt2.sign(payload,
|
|
6764
|
+
return jwt2.sign(payload, env3.SPFN_AUTH_VERIFICATION_TOKEN_SECRET, {
|
|
7155
6765
|
expiresIn: VERIFICATION_TOKEN_EXPIRY,
|
|
7156
6766
|
issuer: "spfn-auth",
|
|
7157
6767
|
audience: "spfn-client"
|
|
@@ -7159,7 +6769,7 @@ function createVerificationToken(payload) {
|
|
|
7159
6769
|
}
|
|
7160
6770
|
function validateVerificationToken(token) {
|
|
7161
6771
|
try {
|
|
7162
|
-
const decoded = jwt2.verify(token,
|
|
6772
|
+
const decoded = jwt2.verify(token, env3.SPFN_AUTH_VERIFICATION_TOKEN_SECRET, {
|
|
7163
6773
|
issuer: "spfn-auth",
|
|
7164
6774
|
audience: "spfn-client"
|
|
7165
6775
|
});
|
|
@@ -7173,17 +6783,14 @@ function validateVerificationToken(token) {
|
|
|
7173
6783
|
}
|
|
7174
6784
|
}
|
|
7175
6785
|
async function sendVerificationEmail(email, code, purpose) {
|
|
7176
|
-
const { subject, text: text10, html } = getVerificationCodeTemplate({
|
|
7177
|
-
code,
|
|
7178
|
-
purpose,
|
|
7179
|
-
expiresInMinutes: VERIFICATION_CODE_EXPIRY_MINUTES
|
|
7180
|
-
});
|
|
7181
6786
|
const result = await sendEmail({
|
|
7182
6787
|
to: email,
|
|
7183
|
-
|
|
7184
|
-
|
|
7185
|
-
|
|
7186
|
-
|
|
6788
|
+
template: "verification-code",
|
|
6789
|
+
data: {
|
|
6790
|
+
code,
|
|
6791
|
+
purpose,
|
|
6792
|
+
expiresInMinutes: VERIFICATION_CODE_EXPIRY_MINUTES
|
|
6793
|
+
}
|
|
7187
6794
|
});
|
|
7188
6795
|
if (!result.success) {
|
|
7189
6796
|
authLogger.email.error("Failed to send verification email", {
|
|
@@ -7194,11 +6801,13 @@ async function sendVerificationEmail(email, code, purpose) {
|
|
|
7194
6801
|
}
|
|
7195
6802
|
}
|
|
7196
6803
|
async function sendVerificationSMS(phone, code, purpose) {
|
|
7197
|
-
const message = `Your verification code is: ${code}`;
|
|
7198
6804
|
const result = await sendSMS({
|
|
7199
|
-
phone,
|
|
7200
|
-
|
|
7201
|
-
|
|
6805
|
+
to: phone,
|
|
6806
|
+
template: "verification-code",
|
|
6807
|
+
data: {
|
|
6808
|
+
code,
|
|
6809
|
+
expiresInMinutes: VERIFICATION_CODE_EXPIRY_MINUTES
|
|
6810
|
+
}
|
|
7202
6811
|
});
|
|
7203
6812
|
if (!result.success) {
|
|
7204
6813
|
authLogger.sms.error("Failed to send verification SMS", {
|
|
@@ -7461,7 +7070,7 @@ init_repositories();
|
|
|
7461
7070
|
init_rbac();
|
|
7462
7071
|
|
|
7463
7072
|
// src/server/lib/config.ts
|
|
7464
|
-
import { env as
|
|
7073
|
+
import { env as env4 } from "@spfn/auth/config";
|
|
7465
7074
|
var COOKIE_NAMES = {
|
|
7466
7075
|
/** Encrypted session data (userId, privateKey, keyId, algorithm) */
|
|
7467
7076
|
SESSION: "spfn_session",
|
|
@@ -7511,7 +7120,7 @@ function getSessionTtl(override) {
|
|
|
7511
7120
|
if (globalConfig.sessionTtl !== void 0) {
|
|
7512
7121
|
return parseDuration(globalConfig.sessionTtl);
|
|
7513
7122
|
}
|
|
7514
|
-
const envTtl =
|
|
7123
|
+
const envTtl = env4.SPFN_AUTH_SESSION_TTL;
|
|
7515
7124
|
if (envTtl) {
|
|
7516
7125
|
return parseDuration(envTtl);
|
|
7517
7126
|
}
|
|
@@ -7673,14 +7282,18 @@ async function hasAllPermissions(userId, permissionNames) {
|
|
|
7673
7282
|
const perms = await getUserPermissions(userId);
|
|
7674
7283
|
return permissionNames.every((p) => perms.includes(p));
|
|
7675
7284
|
}
|
|
7676
|
-
async function
|
|
7285
|
+
async function getUserRole(userId) {
|
|
7677
7286
|
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
7678
7287
|
const user = await usersRepository.findById(userIdNum);
|
|
7679
7288
|
if (!user || !user.roleId) {
|
|
7680
|
-
return
|
|
7289
|
+
return null;
|
|
7681
7290
|
}
|
|
7682
7291
|
const role = await rolesRepository.findById(user.roleId);
|
|
7683
|
-
return role?.name
|
|
7292
|
+
return role?.name || null;
|
|
7293
|
+
}
|
|
7294
|
+
async function hasRole(userId, roleName) {
|
|
7295
|
+
const role = await getUserRole(userId);
|
|
7296
|
+
return role === roleName;
|
|
7684
7297
|
}
|
|
7685
7298
|
async function hasAnyRole(userId, roleNames) {
|
|
7686
7299
|
for (const roleName of roleNames) {
|
|
@@ -7887,6 +7500,65 @@ async function getUserProfileService(userId) {
|
|
|
7887
7500
|
profile
|
|
7888
7501
|
};
|
|
7889
7502
|
}
|
|
7503
|
+
function emptyToNull(value) {
|
|
7504
|
+
if (value === "") {
|
|
7505
|
+
return null;
|
|
7506
|
+
}
|
|
7507
|
+
return value;
|
|
7508
|
+
}
|
|
7509
|
+
async function updateUserProfileService(userId, params) {
|
|
7510
|
+
const userIdNum = typeof userId === "string" ? Number(userId) : Number(userId);
|
|
7511
|
+
const updateData = {};
|
|
7512
|
+
if (params.displayName !== void 0) {
|
|
7513
|
+
updateData.displayName = emptyToNull(params.displayName) || "User";
|
|
7514
|
+
}
|
|
7515
|
+
if (params.firstName !== void 0) {
|
|
7516
|
+
updateData.firstName = emptyToNull(params.firstName);
|
|
7517
|
+
}
|
|
7518
|
+
if (params.lastName !== void 0) {
|
|
7519
|
+
updateData.lastName = emptyToNull(params.lastName);
|
|
7520
|
+
}
|
|
7521
|
+
if (params.avatarUrl !== void 0) {
|
|
7522
|
+
updateData.avatarUrl = emptyToNull(params.avatarUrl);
|
|
7523
|
+
}
|
|
7524
|
+
if (params.bio !== void 0) {
|
|
7525
|
+
updateData.bio = emptyToNull(params.bio);
|
|
7526
|
+
}
|
|
7527
|
+
if (params.locale !== void 0) {
|
|
7528
|
+
updateData.locale = emptyToNull(params.locale) || "en";
|
|
7529
|
+
}
|
|
7530
|
+
if (params.timezone !== void 0) {
|
|
7531
|
+
updateData.timezone = emptyToNull(params.timezone) || "UTC";
|
|
7532
|
+
}
|
|
7533
|
+
if (params.dateOfBirth !== void 0) {
|
|
7534
|
+
updateData.dateOfBirth = emptyToNull(params.dateOfBirth);
|
|
7535
|
+
}
|
|
7536
|
+
if (params.gender !== void 0) {
|
|
7537
|
+
updateData.gender = emptyToNull(params.gender);
|
|
7538
|
+
}
|
|
7539
|
+
if (params.website !== void 0) {
|
|
7540
|
+
updateData.website = emptyToNull(params.website);
|
|
7541
|
+
}
|
|
7542
|
+
if (params.location !== void 0) {
|
|
7543
|
+
updateData.location = emptyToNull(params.location);
|
|
7544
|
+
}
|
|
7545
|
+
if (params.company !== void 0) {
|
|
7546
|
+
updateData.company = emptyToNull(params.company);
|
|
7547
|
+
}
|
|
7548
|
+
if (params.jobTitle !== void 0) {
|
|
7549
|
+
updateData.jobTitle = emptyToNull(params.jobTitle);
|
|
7550
|
+
}
|
|
7551
|
+
if (params.metadata !== void 0) {
|
|
7552
|
+
updateData.metadata = params.metadata;
|
|
7553
|
+
}
|
|
7554
|
+
const existing = await userProfilesRepository.findByUserId(userIdNum);
|
|
7555
|
+
if (!existing && !updateData.displayName) {
|
|
7556
|
+
updateData.displayName = "User";
|
|
7557
|
+
}
|
|
7558
|
+
await userProfilesRepository.upsertByUserId(userIdNum, updateData);
|
|
7559
|
+
const profile = await userProfilesRepository.fetchProfileData(userIdNum);
|
|
7560
|
+
return profile;
|
|
7561
|
+
}
|
|
7890
7562
|
|
|
7891
7563
|
// src/server/routes/auth/index.ts
|
|
7892
7564
|
init_esm();
|
|
@@ -7980,9 +7652,7 @@ var login = route.post("/_auth/login").input({
|
|
|
7980
7652
|
const { body } = await c.data();
|
|
7981
7653
|
return await loginService(body);
|
|
7982
7654
|
});
|
|
7983
|
-
var logout = route.post("/_auth/logout").
|
|
7984
|
-
body: Type.Object({})
|
|
7985
|
-
}).handler(async (c) => {
|
|
7655
|
+
var logout = route.post("/_auth/logout").handler(async (c) => {
|
|
7986
7656
|
const auth = getAuth(c);
|
|
7987
7657
|
if (!auth) {
|
|
7988
7658
|
return c.noContent();
|
|
@@ -7991,9 +7661,7 @@ var logout = route.post("/_auth/logout").input({
|
|
|
7991
7661
|
await logoutService({ userId: Number(userId), keyId });
|
|
7992
7662
|
return c.noContent();
|
|
7993
7663
|
});
|
|
7994
|
-
var rotateKey = route.post("/_auth/keys/rotate").
|
|
7995
|
-
body: Type.Object({})
|
|
7996
|
-
}).interceptor({
|
|
7664
|
+
var rotateKey = route.post("/_auth/keys/rotate").interceptor({
|
|
7997
7665
|
body: Type.Object({
|
|
7998
7666
|
publicKey: Type.String({ description: "New public key" }),
|
|
7999
7667
|
keyId: Type.String({ description: "New key identifier" }),
|
|
@@ -8223,6 +7891,59 @@ var requireRole = defineMiddleware3(
|
|
|
8223
7891
|
}
|
|
8224
7892
|
);
|
|
8225
7893
|
|
|
7894
|
+
// src/server/middleware/role-guard.ts
|
|
7895
|
+
import { defineMiddleware as defineMiddleware4 } from "@spfn/core/route";
|
|
7896
|
+
import { getAuth as getAuth4, getUserRole as getUserRole2, authLogger as authLogger5 } from "@spfn/auth/server";
|
|
7897
|
+
import { ForbiddenError as ForbiddenError3 } from "@spfn/core/errors";
|
|
7898
|
+
import { InsufficientRoleError as InsufficientRoleError2 } from "@spfn/auth/errors";
|
|
7899
|
+
var roleGuard = defineMiddleware4(
|
|
7900
|
+
"roleGuard",
|
|
7901
|
+
(options) => async (c, next) => {
|
|
7902
|
+
const { allow, deny } = options;
|
|
7903
|
+
if (!allow && !deny) {
|
|
7904
|
+
throw new Error("roleGuard requires at least one of: allow, deny");
|
|
7905
|
+
}
|
|
7906
|
+
const auth = getAuth4(c);
|
|
7907
|
+
if (!auth) {
|
|
7908
|
+
authLogger5.middleware.warn("Role guard failed: not authenticated", {
|
|
7909
|
+
path: c.req.path
|
|
7910
|
+
});
|
|
7911
|
+
throw new ForbiddenError3({ message: "Authentication required" });
|
|
7912
|
+
}
|
|
7913
|
+
const { userId } = auth;
|
|
7914
|
+
const userRole = await getUserRole2(userId);
|
|
7915
|
+
if (deny && deny.length > 0) {
|
|
7916
|
+
if (userRole && deny.includes(userRole)) {
|
|
7917
|
+
authLogger5.middleware.warn("Role guard denied", {
|
|
7918
|
+
userId,
|
|
7919
|
+
userRole,
|
|
7920
|
+
deniedRoles: deny,
|
|
7921
|
+
path: c.req.path
|
|
7922
|
+
});
|
|
7923
|
+
throw new InsufficientRoleError2({ requiredRoles: allow || [] });
|
|
7924
|
+
}
|
|
7925
|
+
}
|
|
7926
|
+
if (allow && allow.length > 0) {
|
|
7927
|
+
if (!userRole || !allow.includes(userRole)) {
|
|
7928
|
+
authLogger5.middleware.warn("Role guard failed: role not allowed", {
|
|
7929
|
+
userId,
|
|
7930
|
+
userRole,
|
|
7931
|
+
allowedRoles: allow,
|
|
7932
|
+
path: c.req.path
|
|
7933
|
+
});
|
|
7934
|
+
throw new InsufficientRoleError2({ requiredRoles: allow });
|
|
7935
|
+
}
|
|
7936
|
+
}
|
|
7937
|
+
authLogger5.middleware.debug("Role guard passed", {
|
|
7938
|
+
userId,
|
|
7939
|
+
userRole,
|
|
7940
|
+
allow,
|
|
7941
|
+
deny
|
|
7942
|
+
});
|
|
7943
|
+
await next();
|
|
7944
|
+
}
|
|
7945
|
+
);
|
|
7946
|
+
|
|
8226
7947
|
// src/server/routes/invitations/index.ts
|
|
8227
7948
|
init_types();
|
|
8228
7949
|
init_esm();
|
|
@@ -8416,21 +8137,62 @@ var invitationRouter = defineRouter2({
|
|
|
8416
8137
|
});
|
|
8417
8138
|
|
|
8418
8139
|
// src/server/routes/users/index.ts
|
|
8140
|
+
init_esm();
|
|
8419
8141
|
import { defineRouter as defineRouter3, route as route3 } from "@spfn/core/route";
|
|
8420
8142
|
var getUserProfile = route3.get("/_auth/users/profile").handler(async (c) => {
|
|
8421
8143
|
const { userId } = getAuth(c);
|
|
8422
8144
|
return await getUserProfileService(userId);
|
|
8423
8145
|
});
|
|
8146
|
+
var updateUserProfile = route3.patch("/_auth/users/profile").input({
|
|
8147
|
+
body: Type.Object({
|
|
8148
|
+
displayName: Type.Optional(Type.String({ description: "Display name shown in UI" })),
|
|
8149
|
+
firstName: Type.Optional(Type.String({ description: "First name" })),
|
|
8150
|
+
lastName: Type.Optional(Type.String({ description: "Last name" })),
|
|
8151
|
+
avatarUrl: Type.Optional(Type.String({ description: "Avatar/profile picture URL" })),
|
|
8152
|
+
bio: Type.Optional(Type.String({ description: "Short bio/description" })),
|
|
8153
|
+
locale: Type.Optional(Type.String({ description: "Locale/language preference (e.g., en, ko)" })),
|
|
8154
|
+
timezone: Type.Optional(Type.String({ description: "Timezone (e.g., Asia/Seoul)" })),
|
|
8155
|
+
dateOfBirth: Type.Optional(Type.String({ description: "Date of birth (YYYY-MM-DD)" })),
|
|
8156
|
+
gender: Type.Optional(Type.String({ description: "Gender" })),
|
|
8157
|
+
website: Type.Optional(Type.String({ description: "Personal or professional website" })),
|
|
8158
|
+
location: Type.Optional(Type.String({ description: "Location (city, country, etc.)" })),
|
|
8159
|
+
company: Type.Optional(Type.String({ description: "Company name" })),
|
|
8160
|
+
jobTitle: Type.Optional(Type.String({ description: "Job title" })),
|
|
8161
|
+
metadata: Type.Optional(Type.Record(Type.String(), Type.Any(), { description: "Additional metadata" }))
|
|
8162
|
+
})
|
|
8163
|
+
}).handler(async (c) => {
|
|
8164
|
+
const { userId } = getAuth(c);
|
|
8165
|
+
const { body } = await c.data();
|
|
8166
|
+
return await updateUserProfileService(userId, body);
|
|
8167
|
+
});
|
|
8424
8168
|
var userRouter = defineRouter3({
|
|
8425
|
-
getUserProfile
|
|
8169
|
+
getUserProfile,
|
|
8170
|
+
updateUserProfile
|
|
8426
8171
|
});
|
|
8427
8172
|
|
|
8428
8173
|
// src/server/routes/index.ts
|
|
8429
8174
|
var mainAuthRouter = defineRouter4({
|
|
8430
|
-
//
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
|
|
8175
|
+
// Auth routes
|
|
8176
|
+
checkAccountExists,
|
|
8177
|
+
sendVerificationCode,
|
|
8178
|
+
verifyCode,
|
|
8179
|
+
register,
|
|
8180
|
+
login,
|
|
8181
|
+
logout,
|
|
8182
|
+
rotateKey,
|
|
8183
|
+
changePassword,
|
|
8184
|
+
getAuthSession,
|
|
8185
|
+
// Invitation routes
|
|
8186
|
+
getInvitation,
|
|
8187
|
+
acceptInvitation: acceptInvitation2,
|
|
8188
|
+
createInvitation: createInvitation2,
|
|
8189
|
+
listInvitations: listInvitations2,
|
|
8190
|
+
cancelInvitation: cancelInvitation2,
|
|
8191
|
+
resendInvitation: resendInvitation2,
|
|
8192
|
+
deleteInvitation: deleteInvitation2,
|
|
8193
|
+
// User routes
|
|
8194
|
+
getUserProfile,
|
|
8195
|
+
updateUserProfile
|
|
8434
8196
|
});
|
|
8435
8197
|
|
|
8436
8198
|
// src/server.ts
|
|
@@ -8541,10 +8303,10 @@ function shouldRotateKey(createdAt, rotationDays = 90) {
|
|
|
8541
8303
|
|
|
8542
8304
|
// src/server/lib/session.ts
|
|
8543
8305
|
import * as jose from "jose";
|
|
8544
|
-
import { env as
|
|
8306
|
+
import { env as env5 } from "@spfn/auth/config";
|
|
8545
8307
|
import { env as coreEnv } from "@spfn/core/config";
|
|
8546
8308
|
async function getSessionSecretKey() {
|
|
8547
|
-
const secret =
|
|
8309
|
+
const secret = env5.SPFN_AUTH_SESSION_SECRET;
|
|
8548
8310
|
const encoder = new TextEncoder();
|
|
8549
8311
|
const data = encoder.encode(secret);
|
|
8550
8312
|
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
@@ -8602,14 +8364,14 @@ async function shouldRefreshSession(jwt4, thresholdHours = 24) {
|
|
|
8602
8364
|
}
|
|
8603
8365
|
|
|
8604
8366
|
// src/server/setup.ts
|
|
8605
|
-
import { env as
|
|
8367
|
+
import { env as env6 } from "@spfn/auth/config";
|
|
8606
8368
|
import { getRoleByName as getRoleByName2 } from "@spfn/auth/server";
|
|
8607
8369
|
init_repositories();
|
|
8608
8370
|
function parseAdminAccounts() {
|
|
8609
8371
|
const accounts = [];
|
|
8610
|
-
if (
|
|
8372
|
+
if (env6.SPFN_AUTH_ADMIN_ACCOUNTS) {
|
|
8611
8373
|
try {
|
|
8612
|
-
const accountsJson =
|
|
8374
|
+
const accountsJson = env6.SPFN_AUTH_ADMIN_ACCOUNTS;
|
|
8613
8375
|
const parsed = JSON.parse(accountsJson);
|
|
8614
8376
|
if (!Array.isArray(parsed)) {
|
|
8615
8377
|
authLogger.setup.error("\u274C SPFN_AUTH_ADMIN_ACCOUNTS must be an array");
|
|
@@ -8636,11 +8398,11 @@ function parseAdminAccounts() {
|
|
|
8636
8398
|
return accounts;
|
|
8637
8399
|
}
|
|
8638
8400
|
}
|
|
8639
|
-
const adminEmails =
|
|
8401
|
+
const adminEmails = env6.SPFN_AUTH_ADMIN_EMAILS;
|
|
8640
8402
|
if (adminEmails) {
|
|
8641
8403
|
const emails = adminEmails.split(",").map((s) => s.trim());
|
|
8642
|
-
const passwords = (
|
|
8643
|
-
const roles2 = (
|
|
8404
|
+
const passwords = (env6.SPFN_AUTH_ADMIN_PASSWORDS || "").split(",").map((s) => s.trim());
|
|
8405
|
+
const roles2 = (env6.SPFN_AUTH_ADMIN_ROLES || "").split(",").map((s) => s.trim());
|
|
8644
8406
|
if (passwords.length !== emails.length) {
|
|
8645
8407
|
authLogger.setup.error("\u274C SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch");
|
|
8646
8408
|
return accounts;
|
|
@@ -8662,8 +8424,8 @@ function parseAdminAccounts() {
|
|
|
8662
8424
|
}
|
|
8663
8425
|
return accounts;
|
|
8664
8426
|
}
|
|
8665
|
-
const adminEmail =
|
|
8666
|
-
const adminPassword =
|
|
8427
|
+
const adminEmail = env6.SPFN_AUTH_ADMIN_EMAIL;
|
|
8428
|
+
const adminPassword = env6.SPFN_AUTH_ADMIN_PASSWORD;
|
|
8667
8429
|
if (adminEmail && adminPassword) {
|
|
8668
8430
|
accounts.push({
|
|
8669
8431
|
email: adminEmail,
|
|
@@ -8789,11 +8551,9 @@ export {
|
|
|
8789
8551
|
getAuthConfig,
|
|
8790
8552
|
getAuthSessionService,
|
|
8791
8553
|
getInvitationByToken,
|
|
8792
|
-
getInvitationTemplate,
|
|
8793
8554
|
getInvitationWithDetails,
|
|
8794
8555
|
getKeyId,
|
|
8795
8556
|
getKeySize,
|
|
8796
|
-
getPasswordResetTemplate,
|
|
8797
8557
|
getRoleByName,
|
|
8798
8558
|
getRolePermissions,
|
|
8799
8559
|
getSessionInfo,
|
|
@@ -8805,8 +8565,7 @@ export {
|
|
|
8805
8565
|
getUserId,
|
|
8806
8566
|
getUserPermissions,
|
|
8807
8567
|
getUserProfileService,
|
|
8808
|
-
|
|
8809
|
-
getWelcomeTemplate,
|
|
8568
|
+
getUserRole,
|
|
8810
8569
|
hasAllPermissions,
|
|
8811
8570
|
hasAnyPermission,
|
|
8812
8571
|
hasAnyRole,
|
|
@@ -8822,10 +8581,7 @@ export {
|
|
|
8822
8581
|
parseDuration,
|
|
8823
8582
|
permissions,
|
|
8824
8583
|
permissionsRepository,
|
|
8825
|
-
registerEmailProvider,
|
|
8826
|
-
registerEmailTemplates,
|
|
8827
8584
|
registerPublicKeyService,
|
|
8828
|
-
registerSMSProvider,
|
|
8829
8585
|
registerService,
|
|
8830
8586
|
removePermissionFromRole,
|
|
8831
8587
|
requireAnyPermission,
|
|
@@ -8833,14 +8589,13 @@ export {
|
|
|
8833
8589
|
requireRole,
|
|
8834
8590
|
resendInvitation,
|
|
8835
8591
|
revokeKeyService,
|
|
8592
|
+
roleGuard,
|
|
8836
8593
|
rolePermissions,
|
|
8837
8594
|
rolePermissionsRepository,
|
|
8838
8595
|
roles,
|
|
8839
8596
|
rolesRepository,
|
|
8840
8597
|
rotateKeyService,
|
|
8841
8598
|
sealSession,
|
|
8842
|
-
sendEmail,
|
|
8843
|
-
sendSMS,
|
|
8844
8599
|
sendVerificationCodeService,
|
|
8845
8600
|
setRolePermissions,
|
|
8846
8601
|
shouldRefreshSession,
|
|
@@ -8848,6 +8603,7 @@ export {
|
|
|
8848
8603
|
unsealSession,
|
|
8849
8604
|
updateLastLoginService,
|
|
8850
8605
|
updateRole,
|
|
8606
|
+
updateUserProfileService,
|
|
8851
8607
|
updateUserService,
|
|
8852
8608
|
userInvitations,
|
|
8853
8609
|
userPermissions,
|