@spfn/auth 0.2.0-beta.10 → 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-CRlgoCP5.d.ts → authenticate-CU6_zQaa.d.ts} +182 -182
- package/dist/index.d.ts +143 -130
- package/dist/index.js +24 -1
- package/dist/index.js.map +1 -1
- package/dist/nextjs/server.js +0 -1
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +3 -366
- package/dist/server.js +49 -466
- package/dist/server.js.map +1 -1
- package/package.json +4 -11
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
|
};
|
|
@@ -6701,9 +6695,10 @@ import {
|
|
|
6701
6695
|
} from "@spfn/auth/errors";
|
|
6702
6696
|
|
|
6703
6697
|
// src/server/services/verification.service.ts
|
|
6704
|
-
import { env as
|
|
6698
|
+
import { env as env3 } from "@spfn/auth/config";
|
|
6705
6699
|
import { InvalidVerificationCodeError } from "@spfn/auth/errors";
|
|
6706
6700
|
import jwt2 from "jsonwebtoken";
|
|
6701
|
+
import { sendEmail, sendSMS } from "@spfn/notification/server";
|
|
6707
6702
|
|
|
6708
6703
|
// src/server/logger.ts
|
|
6709
6704
|
import { logger as rootLogger } from "@spfn/core/logger";
|
|
@@ -6723,425 +6718,6 @@ var authLogger = {
|
|
|
6723
6718
|
|
|
6724
6719
|
// src/server/services/verification.service.ts
|
|
6725
6720
|
init_repositories();
|
|
6726
|
-
|
|
6727
|
-
// src/server/services/sms/provider.ts
|
|
6728
|
-
var currentProvider = null;
|
|
6729
|
-
var fallbackProvider = {
|
|
6730
|
-
name: "fallback",
|
|
6731
|
-
sendSMS: async (params) => {
|
|
6732
|
-
authLogger.sms.debug("DEV MODE - SMS not actually sent", {
|
|
6733
|
-
phone: params.phone,
|
|
6734
|
-
message: params.message,
|
|
6735
|
-
purpose: params.purpose || "N/A"
|
|
6736
|
-
});
|
|
6737
|
-
return {
|
|
6738
|
-
success: true,
|
|
6739
|
-
messageId: "dev-mode-no-actual-sms"
|
|
6740
|
-
};
|
|
6741
|
-
}
|
|
6742
|
-
};
|
|
6743
|
-
function registerSMSProvider(provider) {
|
|
6744
|
-
currentProvider = provider;
|
|
6745
|
-
authLogger.sms.info("Registered SMS provider", { name: provider.name });
|
|
6746
|
-
}
|
|
6747
|
-
function getSMSProvider() {
|
|
6748
|
-
return currentProvider || fallbackProvider;
|
|
6749
|
-
}
|
|
6750
|
-
async function sendSMS(params) {
|
|
6751
|
-
const provider = getSMSProvider();
|
|
6752
|
-
return await provider.sendSMS(params);
|
|
6753
|
-
}
|
|
6754
|
-
|
|
6755
|
-
// src/server/services/sms/aws-sns.provider.ts
|
|
6756
|
-
import { env as env3 } from "@spfn/auth/config";
|
|
6757
|
-
function isValidE164Phone(phone) {
|
|
6758
|
-
const e164Regex = /^\+[1-9]\d{1,14}$/;
|
|
6759
|
-
return e164Regex.test(phone);
|
|
6760
|
-
}
|
|
6761
|
-
function createAWSSNSProvider() {
|
|
6762
|
-
try {
|
|
6763
|
-
const { SNSClient, PublishCommand } = __require("@aws-sdk/client-sns");
|
|
6764
|
-
return {
|
|
6765
|
-
name: "aws-sns",
|
|
6766
|
-
sendSMS: async (params) => {
|
|
6767
|
-
const { phone, message, purpose } = params;
|
|
6768
|
-
if (!isValidE164Phone(phone)) {
|
|
6769
|
-
return {
|
|
6770
|
-
success: false,
|
|
6771
|
-
error: "Invalid phone number format. Must be E.164 format (e.g., +821012345678)"
|
|
6772
|
-
};
|
|
6773
|
-
}
|
|
6774
|
-
if (!env3.SPFN_AUTH_AWS_SNS_ACCESS_KEY_ID) {
|
|
6775
|
-
return {
|
|
6776
|
-
success: false,
|
|
6777
|
-
error: "AWS SNS credentials not configured. Set SPFN_AUTH_AWS_SNS_ACCESS_KEY_ID environment variable."
|
|
6778
|
-
};
|
|
6779
|
-
}
|
|
6780
|
-
try {
|
|
6781
|
-
const config = {
|
|
6782
|
-
region: env3.SPFN_AUTH_AWS_REGION || "ap-northeast-2"
|
|
6783
|
-
};
|
|
6784
|
-
if (env3.SPFN_AUTH_AWS_SNS_ACCESS_KEY_ID && env3.SPFN_AUTH_AWS_SNS_SECRET_ACCESS_KEY) {
|
|
6785
|
-
config.credentials = {
|
|
6786
|
-
accessKeyId: env3.SPFN_AUTH_AWS_SNS_ACCESS_KEY_ID,
|
|
6787
|
-
secretAccessKey: env3.SPFN_AUTH_AWS_SNS_SECRET_ACCESS_KEY
|
|
6788
|
-
};
|
|
6789
|
-
}
|
|
6790
|
-
const client = new SNSClient(config);
|
|
6791
|
-
const command = new PublishCommand({
|
|
6792
|
-
PhoneNumber: phone,
|
|
6793
|
-
Message: message,
|
|
6794
|
-
MessageAttributes: {
|
|
6795
|
-
"AWS.SNS.SMS.SMSType": {
|
|
6796
|
-
DataType: "String",
|
|
6797
|
-
StringValue: "Transactional"
|
|
6798
|
-
// For OTP codes
|
|
6799
|
-
},
|
|
6800
|
-
...env3.SPFN_AUTH_AWS_SNS_SENDER_ID && {
|
|
6801
|
-
"AWS.SNS.SMS.SenderID": {
|
|
6802
|
-
DataType: "String",
|
|
6803
|
-
StringValue: env3.SPFN_AUTH_AWS_SNS_SENDER_ID
|
|
6804
|
-
}
|
|
6805
|
-
}
|
|
6806
|
-
}
|
|
6807
|
-
});
|
|
6808
|
-
const response = await client.send(command);
|
|
6809
|
-
authLogger.sms.info("SMS sent via AWS SNS", {
|
|
6810
|
-
phone,
|
|
6811
|
-
messageId: response.MessageId,
|
|
6812
|
-
purpose: purpose || "N/A"
|
|
6813
|
-
});
|
|
6814
|
-
return {
|
|
6815
|
-
success: true,
|
|
6816
|
-
messageId: response.MessageId
|
|
6817
|
-
};
|
|
6818
|
-
} catch (error) {
|
|
6819
|
-
const err = error;
|
|
6820
|
-
authLogger.sms.error("Failed to send SMS via AWS SNS", {
|
|
6821
|
-
phone,
|
|
6822
|
-
error: err.message
|
|
6823
|
-
});
|
|
6824
|
-
return {
|
|
6825
|
-
success: false,
|
|
6826
|
-
error: err.message || "Failed to send SMS via AWS SNS"
|
|
6827
|
-
};
|
|
6828
|
-
}
|
|
6829
|
-
}
|
|
6830
|
-
};
|
|
6831
|
-
} catch (error) {
|
|
6832
|
-
return null;
|
|
6833
|
-
}
|
|
6834
|
-
}
|
|
6835
|
-
var awsSNSProvider = createAWSSNSProvider();
|
|
6836
|
-
|
|
6837
|
-
// src/server/services/sms/index.ts
|
|
6838
|
-
if (awsSNSProvider) {
|
|
6839
|
-
registerSMSProvider(awsSNSProvider);
|
|
6840
|
-
}
|
|
6841
|
-
|
|
6842
|
-
// src/server/services/email/provider.ts
|
|
6843
|
-
var currentProvider2 = null;
|
|
6844
|
-
var fallbackProvider2 = {
|
|
6845
|
-
name: "fallback",
|
|
6846
|
-
sendEmail: async (params) => {
|
|
6847
|
-
authLogger.email.debug("DEV MODE - Email not actually sent", {
|
|
6848
|
-
to: params.to,
|
|
6849
|
-
subject: params.subject,
|
|
6850
|
-
purpose: params.purpose || "N/A",
|
|
6851
|
-
textPreview: params.text?.substring(0, 100) || "N/A"
|
|
6852
|
-
});
|
|
6853
|
-
return {
|
|
6854
|
-
success: true,
|
|
6855
|
-
messageId: "dev-mode-no-actual-email"
|
|
6856
|
-
};
|
|
6857
|
-
}
|
|
6858
|
-
};
|
|
6859
|
-
function registerEmailProvider(provider) {
|
|
6860
|
-
currentProvider2 = provider;
|
|
6861
|
-
authLogger.email.info("Registered email provider", { name: provider.name });
|
|
6862
|
-
}
|
|
6863
|
-
function getEmailProvider() {
|
|
6864
|
-
return currentProvider2 || fallbackProvider2;
|
|
6865
|
-
}
|
|
6866
|
-
async function sendEmail(params) {
|
|
6867
|
-
const provider = getEmailProvider();
|
|
6868
|
-
return await provider.sendEmail(params);
|
|
6869
|
-
}
|
|
6870
|
-
|
|
6871
|
-
// src/server/services/email/aws-ses.provider.ts
|
|
6872
|
-
import { env as env4 } from "@spfn/auth/config";
|
|
6873
|
-
function isValidEmail(email) {
|
|
6874
|
-
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
6875
|
-
return emailRegex.test(email);
|
|
6876
|
-
}
|
|
6877
|
-
function createAWSSESProvider() {
|
|
6878
|
-
return {
|
|
6879
|
-
name: "aws-ses",
|
|
6880
|
-
sendEmail: async (params) => {
|
|
6881
|
-
const { to, subject, text: text10, html, purpose } = params;
|
|
6882
|
-
if (!isValidEmail(to)) {
|
|
6883
|
-
return {
|
|
6884
|
-
success: false,
|
|
6885
|
-
error: "Invalid email address format"
|
|
6886
|
-
};
|
|
6887
|
-
}
|
|
6888
|
-
if (!env4.SPFN_AUTH_AWS_SES_ACCESS_KEY_ID) {
|
|
6889
|
-
authLogger.email.warn("AWS SES credentials not configured", {
|
|
6890
|
-
hint: "Set SPFN_AUTH_AWS_SES_ACCESS_KEY_ID environment variable"
|
|
6891
|
-
});
|
|
6892
|
-
return {
|
|
6893
|
-
success: false,
|
|
6894
|
-
error: "AWS SES credentials not configured. Set SPFN_AUTH_AWS_SES_ACCESS_KEY_ID environment variable."
|
|
6895
|
-
};
|
|
6896
|
-
}
|
|
6897
|
-
if (!env4.SPFN_AUTH_AWS_SES_FROM_EMAIL) {
|
|
6898
|
-
authLogger.email.warn("AWS SES sender email not configured", {
|
|
6899
|
-
hint: "Set SPFN_AUTH_AWS_SES_FROM_EMAIL environment variable"
|
|
6900
|
-
});
|
|
6901
|
-
return {
|
|
6902
|
-
success: false,
|
|
6903
|
-
error: "AWS SES sender email not configured. Set SPFN_AUTH_AWS_SES_FROM_EMAIL environment variable."
|
|
6904
|
-
};
|
|
6905
|
-
}
|
|
6906
|
-
let SESClient;
|
|
6907
|
-
let SendEmailCommand;
|
|
6908
|
-
try {
|
|
6909
|
-
const ses = await import("@aws-sdk/client-ses");
|
|
6910
|
-
SESClient = ses.SESClient;
|
|
6911
|
-
SendEmailCommand = ses.SendEmailCommand;
|
|
6912
|
-
} catch (error) {
|
|
6913
|
-
authLogger.email.warn("@aws-sdk/client-ses not installed", {
|
|
6914
|
-
error: error instanceof Error ? error.message : String(error),
|
|
6915
|
-
hint: "Run: pnpm add @aws-sdk/client-ses"
|
|
6916
|
-
});
|
|
6917
|
-
return {
|
|
6918
|
-
success: false,
|
|
6919
|
-
error: "@aws-sdk/client-ses not installed. Run: pnpm add @aws-sdk/client-ses"
|
|
6920
|
-
};
|
|
6921
|
-
}
|
|
6922
|
-
try {
|
|
6923
|
-
const config = {
|
|
6924
|
-
region: env4.SPFN_AUTH_AWS_REGION || "ap-northeast-2"
|
|
6925
|
-
};
|
|
6926
|
-
if (env4.SPFN_AUTH_AWS_SES_ACCESS_KEY_ID && env4.SPFN_AUTH_AWS_SES_SECRET_ACCESS_KEY) {
|
|
6927
|
-
config.credentials = {
|
|
6928
|
-
accessKeyId: env4.SPFN_AUTH_AWS_SES_ACCESS_KEY_ID,
|
|
6929
|
-
secretAccessKey: env4.SPFN_AUTH_AWS_SES_SECRET_ACCESS_KEY
|
|
6930
|
-
};
|
|
6931
|
-
}
|
|
6932
|
-
const client = new SESClient(config);
|
|
6933
|
-
const body = {};
|
|
6934
|
-
if (text10) {
|
|
6935
|
-
body.Text = {
|
|
6936
|
-
Charset: "UTF-8",
|
|
6937
|
-
Data: text10
|
|
6938
|
-
};
|
|
6939
|
-
}
|
|
6940
|
-
if (html) {
|
|
6941
|
-
body.Html = {
|
|
6942
|
-
Charset: "UTF-8",
|
|
6943
|
-
Data: html
|
|
6944
|
-
};
|
|
6945
|
-
}
|
|
6946
|
-
const command = new SendEmailCommand({
|
|
6947
|
-
Source: env4.SPFN_AUTH_AWS_SES_FROM_EMAIL,
|
|
6948
|
-
Destination: {
|
|
6949
|
-
ToAddresses: [to]
|
|
6950
|
-
},
|
|
6951
|
-
Message: {
|
|
6952
|
-
Subject: {
|
|
6953
|
-
Charset: "UTF-8",
|
|
6954
|
-
Data: subject
|
|
6955
|
-
},
|
|
6956
|
-
Body: body
|
|
6957
|
-
}
|
|
6958
|
-
});
|
|
6959
|
-
const response = await client.send(command);
|
|
6960
|
-
authLogger.email.info("Email sent via AWS SES", {
|
|
6961
|
-
to,
|
|
6962
|
-
messageId: response.MessageId,
|
|
6963
|
-
purpose: purpose || "N/A"
|
|
6964
|
-
});
|
|
6965
|
-
return {
|
|
6966
|
-
success: true,
|
|
6967
|
-
messageId: response.MessageId
|
|
6968
|
-
};
|
|
6969
|
-
} catch (error) {
|
|
6970
|
-
const err = error;
|
|
6971
|
-
authLogger.email.error("Failed to send email via AWS SES", {
|
|
6972
|
-
to,
|
|
6973
|
-
error: err.message
|
|
6974
|
-
});
|
|
6975
|
-
return {
|
|
6976
|
-
success: false,
|
|
6977
|
-
error: err.message || "Failed to send email via AWS SES"
|
|
6978
|
-
};
|
|
6979
|
-
}
|
|
6980
|
-
}
|
|
6981
|
-
};
|
|
6982
|
-
}
|
|
6983
|
-
var awsSESProvider = createAWSSESProvider();
|
|
6984
|
-
|
|
6985
|
-
// src/server/services/email/index.ts
|
|
6986
|
-
registerEmailProvider(awsSESProvider);
|
|
6987
|
-
|
|
6988
|
-
// src/server/services/email/templates/verification-code.ts
|
|
6989
|
-
function getSubject(purpose) {
|
|
6990
|
-
switch (purpose) {
|
|
6991
|
-
case "registration":
|
|
6992
|
-
return "Verify your email address";
|
|
6993
|
-
case "login":
|
|
6994
|
-
return "Your login verification code";
|
|
6995
|
-
case "password_reset":
|
|
6996
|
-
return "Reset your password";
|
|
6997
|
-
default:
|
|
6998
|
-
return "Your verification code";
|
|
6999
|
-
}
|
|
7000
|
-
}
|
|
7001
|
-
function getPurposeText(purpose) {
|
|
7002
|
-
switch (purpose) {
|
|
7003
|
-
case "registration":
|
|
7004
|
-
return "complete your registration";
|
|
7005
|
-
case "login":
|
|
7006
|
-
return "verify your identity";
|
|
7007
|
-
case "password_reset":
|
|
7008
|
-
return "reset your password";
|
|
7009
|
-
default:
|
|
7010
|
-
return "verify your identity";
|
|
7011
|
-
}
|
|
7012
|
-
}
|
|
7013
|
-
function generateText(params) {
|
|
7014
|
-
const { code, expiresInMinutes = 5 } = params;
|
|
7015
|
-
return `Your verification code is: ${code}
|
|
7016
|
-
|
|
7017
|
-
This code will expire in ${expiresInMinutes} minutes.
|
|
7018
|
-
|
|
7019
|
-
If you didn't request this code, please ignore this email.`;
|
|
7020
|
-
}
|
|
7021
|
-
function generateHTML(params) {
|
|
7022
|
-
const { code, purpose, expiresInMinutes = 5, appName } = params;
|
|
7023
|
-
const purposeText = getPurposeText(purpose);
|
|
7024
|
-
return `<!DOCTYPE html>
|
|
7025
|
-
<html>
|
|
7026
|
-
<head>
|
|
7027
|
-
<meta charset="utf-8">
|
|
7028
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7029
|
-
<title>Verification Code</title>
|
|
7030
|
-
</head>
|
|
7031
|
-
<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;">
|
|
7032
|
-
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; border-radius: 10px 10px 0 0; text-align: center;">
|
|
7033
|
-
<h1 style="color: white; margin: 0; font-size: 24px;">${appName ? appName : "Verification Code"}</h1>
|
|
7034
|
-
</div>
|
|
7035
|
-
<div style="background: #ffffff; padding: 30px; border: 1px solid #e0e0e0; border-top: none; border-radius: 0 0 10px 10px;">
|
|
7036
|
-
<p style="margin-bottom: 20px; font-size: 16px;">
|
|
7037
|
-
Please use the following verification code to ${purposeText}:
|
|
7038
|
-
</p>
|
|
7039
|
-
<div style="background: #f8f9fa; padding: 25px; border-radius: 8px; text-align: center; margin: 25px 0; border: 2px dashed #dee2e6;">
|
|
7040
|
-
<span style="font-size: 36px; font-weight: bold; letter-spacing: 10px; color: #333; font-family: 'Courier New', monospace;">${code}</span>
|
|
7041
|
-
</div>
|
|
7042
|
-
<p style="color: #666; font-size: 14px; margin-top: 20px; text-align: center;">
|
|
7043
|
-
<strong>This code will expire in ${expiresInMinutes} minutes.</strong>
|
|
7044
|
-
</p>
|
|
7045
|
-
<hr style="border: none; border-top: 1px solid #eee; margin: 30px 0;">
|
|
7046
|
-
<p style="color: #999; font-size: 12px; text-align: center; margin: 0;">
|
|
7047
|
-
If you didn't request this code, please ignore this email.
|
|
7048
|
-
</p>
|
|
7049
|
-
</div>
|
|
7050
|
-
<div style="text-align: center; padding: 20px; color: #999; font-size: 11px;">
|
|
7051
|
-
<p style="margin: 0;">This is an automated message. Please do not reply.</p>
|
|
7052
|
-
</div>
|
|
7053
|
-
</body>
|
|
7054
|
-
</html>`;
|
|
7055
|
-
}
|
|
7056
|
-
function verificationCodeTemplate(params) {
|
|
7057
|
-
return {
|
|
7058
|
-
subject: getSubject(params.purpose),
|
|
7059
|
-
text: generateText(params),
|
|
7060
|
-
html: generateHTML(params)
|
|
7061
|
-
};
|
|
7062
|
-
}
|
|
7063
|
-
|
|
7064
|
-
// src/server/services/email/templates/registry.ts
|
|
7065
|
-
var customTemplates = {};
|
|
7066
|
-
function registerEmailTemplates(templates) {
|
|
7067
|
-
customTemplates = { ...customTemplates, ...templates };
|
|
7068
|
-
authLogger.email.info("Registered custom email templates", {
|
|
7069
|
-
templates: Object.keys(templates)
|
|
7070
|
-
});
|
|
7071
|
-
}
|
|
7072
|
-
function getVerificationCodeTemplate(params) {
|
|
7073
|
-
if (customTemplates.verificationCode) {
|
|
7074
|
-
return customTemplates.verificationCode(params);
|
|
7075
|
-
}
|
|
7076
|
-
return verificationCodeTemplate(params);
|
|
7077
|
-
}
|
|
7078
|
-
function getWelcomeTemplate(params) {
|
|
7079
|
-
if (customTemplates.welcome) {
|
|
7080
|
-
return customTemplates.welcome(params);
|
|
7081
|
-
}
|
|
7082
|
-
return {
|
|
7083
|
-
subject: params.appName ? `Welcome to ${params.appName}!` : "Welcome!",
|
|
7084
|
-
text: `Welcome! Your account has been created successfully.`,
|
|
7085
|
-
html: `
|
|
7086
|
-
<!DOCTYPE html>
|
|
7087
|
-
<html>
|
|
7088
|
-
<head><meta charset="utf-8"></head>
|
|
7089
|
-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px;">
|
|
7090
|
-
<h1>Welcome${params.appName ? ` to ${params.appName}` : ""}!</h1>
|
|
7091
|
-
<p>Your account has been created successfully.</p>
|
|
7092
|
-
</body>
|
|
7093
|
-
</html>`
|
|
7094
|
-
};
|
|
7095
|
-
}
|
|
7096
|
-
function getPasswordResetTemplate(params) {
|
|
7097
|
-
if (customTemplates.passwordReset) {
|
|
7098
|
-
return customTemplates.passwordReset(params);
|
|
7099
|
-
}
|
|
7100
|
-
const expires = params.expiresInMinutes || 30;
|
|
7101
|
-
return {
|
|
7102
|
-
subject: "Reset your password",
|
|
7103
|
-
text: `Click this link to reset your password: ${params.resetLink}
|
|
7104
|
-
|
|
7105
|
-
This link will expire in ${expires} minutes.`,
|
|
7106
|
-
html: `
|
|
7107
|
-
<!DOCTYPE html>
|
|
7108
|
-
<html>
|
|
7109
|
-
<head><meta charset="utf-8"></head>
|
|
7110
|
-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px;">
|
|
7111
|
-
<h1>Reset Your Password</h1>
|
|
7112
|
-
<p>Click the button below to reset your password:</p>
|
|
7113
|
-
<a href="${params.resetLink}" style="display: inline-block; background: #667eea; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px;">Reset Password</a>
|
|
7114
|
-
<p style="color: #666; margin-top: 20px;">This link will expire in ${expires} minutes.</p>
|
|
7115
|
-
</body>
|
|
7116
|
-
</html>`
|
|
7117
|
-
};
|
|
7118
|
-
}
|
|
7119
|
-
function getInvitationTemplate(params) {
|
|
7120
|
-
if (customTemplates.invitation) {
|
|
7121
|
-
return customTemplates.invitation(params);
|
|
7122
|
-
}
|
|
7123
|
-
const appName = params.appName || "our platform";
|
|
7124
|
-
const inviterText = params.inviterName ? `${params.inviterName} has invited you` : "You have been invited";
|
|
7125
|
-
const roleText = params.roleName ? ` as ${params.roleName}` : "";
|
|
7126
|
-
return {
|
|
7127
|
-
subject: `You're invited to join ${appName}`,
|
|
7128
|
-
text: `${inviterText} to join ${appName}${roleText}.
|
|
7129
|
-
|
|
7130
|
-
Click here to accept: ${params.inviteLink}`,
|
|
7131
|
-
html: `
|
|
7132
|
-
<!DOCTYPE html>
|
|
7133
|
-
<html>
|
|
7134
|
-
<head><meta charset="utf-8"></head>
|
|
7135
|
-
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; padding: 20px;">
|
|
7136
|
-
<h1>You're Invited!</h1>
|
|
7137
|
-
<p>${inviterText} to join <strong>${appName}</strong>${roleText}.</p>
|
|
7138
|
-
<a href="${params.inviteLink}" style="display: inline-block; background: #667eea; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px;">Accept Invitation</a>
|
|
7139
|
-
</body>
|
|
7140
|
-
</html>`
|
|
7141
|
-
};
|
|
7142
|
-
}
|
|
7143
|
-
|
|
7144
|
-
// src/server/services/verification.service.ts
|
|
7145
6721
|
var VERIFICATION_TOKEN_EXPIRY = "15m";
|
|
7146
6722
|
var VERIFICATION_CODE_EXPIRY_MINUTES = 5;
|
|
7147
6723
|
var MAX_VERIFICATION_ATTEMPTS = 5;
|
|
@@ -7185,7 +6761,7 @@ async function markCodeAsUsed(codeId) {
|
|
|
7185
6761
|
await verificationCodesRepository.markAsUsed(codeId);
|
|
7186
6762
|
}
|
|
7187
6763
|
function createVerificationToken(payload) {
|
|
7188
|
-
return jwt2.sign(payload,
|
|
6764
|
+
return jwt2.sign(payload, env3.SPFN_AUTH_VERIFICATION_TOKEN_SECRET, {
|
|
7189
6765
|
expiresIn: VERIFICATION_TOKEN_EXPIRY,
|
|
7190
6766
|
issuer: "spfn-auth",
|
|
7191
6767
|
audience: "spfn-client"
|
|
@@ -7193,7 +6769,7 @@ function createVerificationToken(payload) {
|
|
|
7193
6769
|
}
|
|
7194
6770
|
function validateVerificationToken(token) {
|
|
7195
6771
|
try {
|
|
7196
|
-
const decoded = jwt2.verify(token,
|
|
6772
|
+
const decoded = jwt2.verify(token, env3.SPFN_AUTH_VERIFICATION_TOKEN_SECRET, {
|
|
7197
6773
|
issuer: "spfn-auth",
|
|
7198
6774
|
audience: "spfn-client"
|
|
7199
6775
|
});
|
|
@@ -7207,17 +6783,14 @@ function validateVerificationToken(token) {
|
|
|
7207
6783
|
}
|
|
7208
6784
|
}
|
|
7209
6785
|
async function sendVerificationEmail(email, code, purpose) {
|
|
7210
|
-
const { subject, text: text10, html } = getVerificationCodeTemplate({
|
|
7211
|
-
code,
|
|
7212
|
-
purpose,
|
|
7213
|
-
expiresInMinutes: VERIFICATION_CODE_EXPIRY_MINUTES
|
|
7214
|
-
});
|
|
7215
6786
|
const result = await sendEmail({
|
|
7216
6787
|
to: email,
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
6788
|
+
template: "verification-code",
|
|
6789
|
+
data: {
|
|
6790
|
+
code,
|
|
6791
|
+
purpose,
|
|
6792
|
+
expiresInMinutes: VERIFICATION_CODE_EXPIRY_MINUTES
|
|
6793
|
+
}
|
|
7221
6794
|
});
|
|
7222
6795
|
if (!result.success) {
|
|
7223
6796
|
authLogger.email.error("Failed to send verification email", {
|
|
@@ -7228,11 +6801,13 @@ async function sendVerificationEmail(email, code, purpose) {
|
|
|
7228
6801
|
}
|
|
7229
6802
|
}
|
|
7230
6803
|
async function sendVerificationSMS(phone, code, purpose) {
|
|
7231
|
-
const message = `Your verification code is: ${code}`;
|
|
7232
6804
|
const result = await sendSMS({
|
|
7233
|
-
phone,
|
|
7234
|
-
|
|
7235
|
-
|
|
6805
|
+
to: phone,
|
|
6806
|
+
template: "verification-code",
|
|
6807
|
+
data: {
|
|
6808
|
+
code,
|
|
6809
|
+
expiresInMinutes: VERIFICATION_CODE_EXPIRY_MINUTES
|
|
6810
|
+
}
|
|
7236
6811
|
});
|
|
7237
6812
|
if (!result.success) {
|
|
7238
6813
|
authLogger.sms.error("Failed to send verification SMS", {
|
|
@@ -7495,7 +7070,7 @@ init_repositories();
|
|
|
7495
7070
|
init_rbac();
|
|
7496
7071
|
|
|
7497
7072
|
// src/server/lib/config.ts
|
|
7498
|
-
import { env as
|
|
7073
|
+
import { env as env4 } from "@spfn/auth/config";
|
|
7499
7074
|
var COOKIE_NAMES = {
|
|
7500
7075
|
/** Encrypted session data (userId, privateKey, keyId, algorithm) */
|
|
7501
7076
|
SESSION: "spfn_session",
|
|
@@ -7545,7 +7120,7 @@ function getSessionTtl(override) {
|
|
|
7545
7120
|
if (globalConfig.sessionTtl !== void 0) {
|
|
7546
7121
|
return parseDuration(globalConfig.sessionTtl);
|
|
7547
7122
|
}
|
|
7548
|
-
const envTtl =
|
|
7123
|
+
const envTtl = env4.SPFN_AUTH_SESSION_TTL;
|
|
7549
7124
|
if (envTtl) {
|
|
7550
7125
|
return parseDuration(envTtl);
|
|
7551
7126
|
}
|
|
@@ -8597,10 +8172,27 @@ var userRouter = defineRouter3({
|
|
|
8597
8172
|
|
|
8598
8173
|
// src/server/routes/index.ts
|
|
8599
8174
|
var mainAuthRouter = defineRouter4({
|
|
8600
|
-
//
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
|
|
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
|
|
8604
8196
|
});
|
|
8605
8197
|
|
|
8606
8198
|
// src/server.ts
|
|
@@ -8711,10 +8303,10 @@ function shouldRotateKey(createdAt, rotationDays = 90) {
|
|
|
8711
8303
|
|
|
8712
8304
|
// src/server/lib/session.ts
|
|
8713
8305
|
import * as jose from "jose";
|
|
8714
|
-
import { env as
|
|
8306
|
+
import { env as env5 } from "@spfn/auth/config";
|
|
8715
8307
|
import { env as coreEnv } from "@spfn/core/config";
|
|
8716
8308
|
async function getSessionSecretKey() {
|
|
8717
|
-
const secret =
|
|
8309
|
+
const secret = env5.SPFN_AUTH_SESSION_SECRET;
|
|
8718
8310
|
const encoder = new TextEncoder();
|
|
8719
8311
|
const data = encoder.encode(secret);
|
|
8720
8312
|
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
@@ -8772,14 +8364,14 @@ async function shouldRefreshSession(jwt4, thresholdHours = 24) {
|
|
|
8772
8364
|
}
|
|
8773
8365
|
|
|
8774
8366
|
// src/server/setup.ts
|
|
8775
|
-
import { env as
|
|
8367
|
+
import { env as env6 } from "@spfn/auth/config";
|
|
8776
8368
|
import { getRoleByName as getRoleByName2 } from "@spfn/auth/server";
|
|
8777
8369
|
init_repositories();
|
|
8778
8370
|
function parseAdminAccounts() {
|
|
8779
8371
|
const accounts = [];
|
|
8780
|
-
if (
|
|
8372
|
+
if (env6.SPFN_AUTH_ADMIN_ACCOUNTS) {
|
|
8781
8373
|
try {
|
|
8782
|
-
const accountsJson =
|
|
8374
|
+
const accountsJson = env6.SPFN_AUTH_ADMIN_ACCOUNTS;
|
|
8783
8375
|
const parsed = JSON.parse(accountsJson);
|
|
8784
8376
|
if (!Array.isArray(parsed)) {
|
|
8785
8377
|
authLogger.setup.error("\u274C SPFN_AUTH_ADMIN_ACCOUNTS must be an array");
|
|
@@ -8806,11 +8398,11 @@ function parseAdminAccounts() {
|
|
|
8806
8398
|
return accounts;
|
|
8807
8399
|
}
|
|
8808
8400
|
}
|
|
8809
|
-
const adminEmails =
|
|
8401
|
+
const adminEmails = env6.SPFN_AUTH_ADMIN_EMAILS;
|
|
8810
8402
|
if (adminEmails) {
|
|
8811
8403
|
const emails = adminEmails.split(",").map((s) => s.trim());
|
|
8812
|
-
const passwords = (
|
|
8813
|
-
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());
|
|
8814
8406
|
if (passwords.length !== emails.length) {
|
|
8815
8407
|
authLogger.setup.error("\u274C SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch");
|
|
8816
8408
|
return accounts;
|
|
@@ -8832,8 +8424,8 @@ function parseAdminAccounts() {
|
|
|
8832
8424
|
}
|
|
8833
8425
|
return accounts;
|
|
8834
8426
|
}
|
|
8835
|
-
const adminEmail =
|
|
8836
|
-
const adminPassword =
|
|
8427
|
+
const adminEmail = env6.SPFN_AUTH_ADMIN_EMAIL;
|
|
8428
|
+
const adminPassword = env6.SPFN_AUTH_ADMIN_PASSWORD;
|
|
8837
8429
|
if (adminEmail && adminPassword) {
|
|
8838
8430
|
accounts.push({
|
|
8839
8431
|
email: adminEmail,
|
|
@@ -8959,11 +8551,9 @@ export {
|
|
|
8959
8551
|
getAuthConfig,
|
|
8960
8552
|
getAuthSessionService,
|
|
8961
8553
|
getInvitationByToken,
|
|
8962
|
-
getInvitationTemplate,
|
|
8963
8554
|
getInvitationWithDetails,
|
|
8964
8555
|
getKeyId,
|
|
8965
8556
|
getKeySize,
|
|
8966
|
-
getPasswordResetTemplate,
|
|
8967
8557
|
getRoleByName,
|
|
8968
8558
|
getRolePermissions,
|
|
8969
8559
|
getSessionInfo,
|
|
@@ -8976,8 +8566,6 @@ export {
|
|
|
8976
8566
|
getUserPermissions,
|
|
8977
8567
|
getUserProfileService,
|
|
8978
8568
|
getUserRole,
|
|
8979
|
-
getVerificationCodeTemplate,
|
|
8980
|
-
getWelcomeTemplate,
|
|
8981
8569
|
hasAllPermissions,
|
|
8982
8570
|
hasAnyPermission,
|
|
8983
8571
|
hasAnyRole,
|
|
@@ -8993,10 +8581,7 @@ export {
|
|
|
8993
8581
|
parseDuration,
|
|
8994
8582
|
permissions,
|
|
8995
8583
|
permissionsRepository,
|
|
8996
|
-
registerEmailProvider,
|
|
8997
|
-
registerEmailTemplates,
|
|
8998
8584
|
registerPublicKeyService,
|
|
8999
|
-
registerSMSProvider,
|
|
9000
8585
|
registerService,
|
|
9001
8586
|
removePermissionFromRole,
|
|
9002
8587
|
requireAnyPermission,
|
|
@@ -9011,8 +8596,6 @@ export {
|
|
|
9011
8596
|
rolesRepository,
|
|
9012
8597
|
rotateKeyService,
|
|
9013
8598
|
sealSession,
|
|
9014
|
-
sendEmail,
|
|
9015
|
-
sendSMS,
|
|
9016
8599
|
sendVerificationCodeService,
|
|
9017
8600
|
setRolePermissions,
|
|
9018
8601
|
shouldRefreshSession,
|