@authcore/core 0.6.0 → 0.12.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.
Files changed (87) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +141 -125
  3. package/dist/auth.d.ts +141 -13
  4. package/dist/auth.d.ts.map +1 -1
  5. package/dist/auth.js +265 -7
  6. package/dist/auth.js.map +1 -1
  7. package/dist/features/emailVerification.d.ts +6 -2
  8. package/dist/features/emailVerification.d.ts.map +1 -1
  9. package/dist/features/emailVerification.js +7 -10
  10. package/dist/features/emailVerification.js.map +1 -1
  11. package/dist/features/invitation.d.ts +7 -2
  12. package/dist/features/invitation.d.ts.map +1 -1
  13. package/dist/features/invitation.js +7 -9
  14. package/dist/features/invitation.js.map +1 -1
  15. package/dist/features/magicLink.d.ts +56 -0
  16. package/dist/features/magicLink.d.ts.map +1 -0
  17. package/dist/features/magicLink.js +88 -0
  18. package/dist/features/magicLink.js.map +1 -0
  19. package/dist/features/oauth.d.ts +39 -0
  20. package/dist/features/oauth.d.ts.map +1 -0
  21. package/dist/features/oauth.js +161 -0
  22. package/dist/features/oauth.js.map +1 -0
  23. package/dist/features/passwordReset.d.ts +6 -2
  24. package/dist/features/passwordReset.d.ts.map +1 -1
  25. package/dist/features/passwordReset.js +7 -10
  26. package/dist/features/passwordReset.js.map +1 -1
  27. package/dist/features/refresh.d.ts +41 -0
  28. package/dist/features/refresh.d.ts.map +1 -0
  29. package/dist/features/refresh.js +58 -0
  30. package/dist/features/refresh.js.map +1 -0
  31. package/dist/features/templates.d.ts +46 -0
  32. package/dist/features/templates.d.ts.map +1 -0
  33. package/dist/features/templates.js +67 -0
  34. package/dist/features/templates.js.map +1 -0
  35. package/dist/features/twoFactor.d.ts +72 -0
  36. package/dist/features/twoFactor.d.ts.map +1 -0
  37. package/dist/features/twoFactor.js +119 -0
  38. package/dist/features/twoFactor.js.map +1 -0
  39. package/dist/index.d.ts +22 -8
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +14 -2
  42. package/dist/index.js.map +1 -1
  43. package/dist/oauth/apple.d.ts +80 -0
  44. package/dist/oauth/apple.d.ts.map +1 -0
  45. package/dist/oauth/apple.js +148 -0
  46. package/dist/oauth/apple.js.map +1 -0
  47. package/dist/oauth/discord.d.ts +32 -0
  48. package/dist/oauth/discord.d.ts.map +1 -0
  49. package/dist/oauth/discord.js +86 -0
  50. package/dist/oauth/discord.js.map +1 -0
  51. package/dist/oauth/github.d.ts +35 -0
  52. package/dist/oauth/github.d.ts.map +1 -0
  53. package/dist/oauth/github.js +114 -0
  54. package/dist/oauth/github.js.map +1 -0
  55. package/dist/oauth/google.d.ts +21 -0
  56. package/dist/oauth/google.d.ts.map +1 -0
  57. package/dist/oauth/google.js +76 -0
  58. package/dist/oauth/google.js.map +1 -0
  59. package/dist/oauth/microsoft.d.ts +40 -0
  60. package/dist/oauth/microsoft.d.ts.map +1 -0
  61. package/dist/oauth/microsoft.js +126 -0
  62. package/dist/oauth/microsoft.js.map +1 -0
  63. package/dist/utils/token.d.ts +37 -0
  64. package/dist/utils/token.d.ts.map +1 -1
  65. package/dist/utils/token.js +53 -0
  66. package/dist/utils/token.js.map +1 -1
  67. package/dist/utils/totp.d.ts +59 -0
  68. package/dist/utils/totp.d.ts.map +1 -0
  69. package/dist/utils/totp.js +176 -0
  70. package/dist/utils/totp.js.map +1 -0
  71. package/dist/utils/validation.d.ts +18 -0
  72. package/dist/utils/validation.d.ts.map +1 -1
  73. package/dist/utils/validation.js +8 -0
  74. package/dist/utils/validation.js.map +1 -1
  75. package/package.json +4 -3
  76. package/dist/adapters/database.interface.d.ts +0 -42
  77. package/dist/adapters/database.interface.d.ts.map +0 -1
  78. package/dist/adapters/database.interface.js +0 -2
  79. package/dist/adapters/database.interface.js.map +0 -1
  80. package/dist/adapters/email.interface.d.ts +0 -31
  81. package/dist/adapters/email.interface.d.ts.map +0 -1
  82. package/dist/adapters/email.interface.js +0 -2
  83. package/dist/adapters/email.interface.js.map +0 -1
  84. package/dist/types.d.ts +0 -76
  85. package/dist/types.d.ts.map +0 -1
  86. package/dist/types.js +0 -6
  87. package/dist/types.js.map +0 -1
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Default email-template render functions. These match the inline content
3
+ * shipped before 0.10 byte-for-byte so apps that don't override anything see
4
+ * no behavior change.
5
+ *
6
+ * Consumers override individual templates via `EmailConfig.templates`:
7
+ *
8
+ * ```ts
9
+ * createAuth({
10
+ * email: {
11
+ * provider: resendAdapter(...),
12
+ * from: 'auth@app.com',
13
+ * templates: {
14
+ * resetPassword: ({ link, email, ttlHours }) => ({
15
+ * subject: 'Reset your password',
16
+ * html: `<p>Hi ${email},</p><p><a href="${link}">Reset</a></p>`,
17
+ * text: `Reset your password: ${link}`,
18
+ * }),
19
+ * },
20
+ * },
21
+ * })
22
+ * ```
23
+ */
24
+ export const defaultVerifyEmailTemplate = ({ link, ttlHours }) => ({
25
+ subject: 'Verify your email address',
26
+ html: `
27
+ <p>Hello,</p>
28
+ <p>Please verify your email address by clicking the link below:</p>
29
+ <p><a href="${link}">${link}</a></p>
30
+ <p>This link expires in ${ttlHours} hours.</p>
31
+ <p>If you did not create an account, you can ignore this email.</p>
32
+ `,
33
+ text: `Please verify your email by visiting: ${link}\n\nThis link expires in ${ttlHours} hours.`,
34
+ });
35
+ export const defaultResetPasswordTemplate = ({ link, ttlHours }) => ({
36
+ subject: 'Reset your password',
37
+ html: `
38
+ <p>Hello,</p>
39
+ <p>We received a request to reset your password. Click the link below to proceed:</p>
40
+ <p><a href="${link}">${link}</a></p>
41
+ <p>This link expires in ${ttlHours} hour${ttlHours === 1 ? '' : 's'}.</p>
42
+ <p>If you did not request a password reset, you can ignore this email.</p>
43
+ `,
44
+ text: `Reset your password by visiting: ${link}\n\nThis link expires in ${ttlHours} hour${ttlHours === 1 ? '' : 's'}.`,
45
+ });
46
+ export const defaultInvitationTemplate = ({ link, ttlHours }) => ({
47
+ subject: 'You have been invited',
48
+ html: `
49
+ <p>Hello,</p>
50
+ <p>You have been invited to create an account. Click the link below to set your password:</p>
51
+ <p><a href="${link}">${link}</a></p>
52
+ <p>This link expires in ${ttlHours} hours.</p>
53
+ `,
54
+ text: `You have been invited. Set your password by visiting: ${link}\n\nThis link expires in ${ttlHours} hours.`,
55
+ });
56
+ export const defaultMagicLinkTemplate = ({ link, ttlMinutes }) => ({
57
+ subject: 'Sign in to your account',
58
+ html: `
59
+ <p>Hello,</p>
60
+ <p>Click the link below to sign in:</p>
61
+ <p><a href="${link}">${link}</a></p>
62
+ <p>This link expires in ${ttlMinutes} minutes and can only be used once.</p>
63
+ <p>If you did not request this email, you can safely ignore it.</p>
64
+ `,
65
+ text: `Sign in by visiting: ${link}\n\nThis link expires in ${ttlMinutes} minutes and can only be used once.\n\nIf you did not request this email, you can safely ignore it.`,
66
+ });
67
+ //# sourceMappingURL=templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/features/templates.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAIlC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5B,OAAO,EAAE,2BAA2B;IACpC,IAAI,EAAE;;;oBAGY,IAAI,KAAK,IAAI;gCACD,QAAQ;;KAEnC;IACH,IAAI,EAAE,yCAAyC,IAAI,4BAA4B,QAAQ,SAAS;CACjG,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAIpC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5B,OAAO,EAAE,qBAAqB;IAC9B,IAAI,EAAE;;;oBAGY,IAAI,KAAK,IAAI;gCACD,QAAQ,QAAQ,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG;;KAEpE;IACH,IAAI,EAAE,oCAAoC,IAAI,4BAA4B,QAAQ,QAAQ,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG;CACvH,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,yBAAyB,GAKjC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;IAC5B,OAAO,EAAE,uBAAuB;IAChC,IAAI,EAAE;;;oBAGY,IAAI,KAAK,IAAI;gCACD,QAAQ;KACnC;IACH,IAAI,EAAE,yDAAyD,IAAI,4BAA4B,QAAQ,SAAS;CACjH,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAIhC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC;IAC9B,OAAO,EAAE,yBAAyB;IAClC,IAAI,EAAE;;;oBAGY,IAAI,KAAK,IAAI;gCACD,UAAU;;KAErC;IACH,IAAI,EAAE,wBAAwB,IAAI,4BAA4B,UAAU,qGAAqG;CAC9K,CAAC,CAAA"}
@@ -0,0 +1,72 @@
1
+ import type { DatabaseAdapter, User } from '@authcore/types';
2
+ /**
3
+ * Begin 2FA enrollment for a user.
4
+ *
5
+ * Generates a fresh TOTP secret + a fresh set of recovery codes. The secret is
6
+ * stored on the user immediately so a `setup → enable` sequence on the same
7
+ * device works, but `twoFactorEnabled` stays `false` until `enableTwoFactor`
8
+ * verifies the first code from the authenticator app.
9
+ *
10
+ * Recovery codes are SHA-256 hashed before storage (same pattern as every
11
+ * other AuthCore token). The raw codes are returned ONCE — the caller MUST
12
+ * show them to the user, who copies them down.
13
+ */
14
+ export declare function setupTwoFactor(params: {
15
+ userId: string;
16
+ email: string;
17
+ issuer: string;
18
+ db: DatabaseAdapter;
19
+ }): Promise<{
20
+ secret: string;
21
+ otpauthUrl: string;
22
+ recoveryCodes: string[];
23
+ }>;
24
+ /**
25
+ * Enable 2FA for a user. Requires a valid TOTP code matching the secret stored
26
+ * during {@link setupTwoFactor}. Idempotent against the already-enabled state.
27
+ *
28
+ * @throws Error('TWO_FACTOR_NOT_SET_UP') if setup hasn't run.
29
+ * @throws Error('INVALID_TWO_FACTOR_CODE') if the code doesn't match.
30
+ */
31
+ export declare function enableTwoFactor(params: {
32
+ userId: string;
33
+ code: string;
34
+ db: DatabaseAdapter;
35
+ }): Promise<void>;
36
+ /**
37
+ * Disable 2FA for a user. Clears the secret + all recovery codes + the enabled flag.
38
+ *
39
+ * Callers are responsible for additional confirmation (e.g. password re-entry) —
40
+ * this function trusts that the caller has already authorized the action.
41
+ */
42
+ export declare function disableTwoFactor(params: {
43
+ userId: string;
44
+ db: DatabaseAdapter;
45
+ }): Promise<void>;
46
+ /**
47
+ * Verify a TOTP code as the second factor in a login challenge.
48
+ * Returns the user record on success.
49
+ *
50
+ * @throws Error('USER_NOT_FOUND') if no user with that id.
51
+ * @throws Error('TWO_FACTOR_NOT_ENABLED') if the user has not enrolled.
52
+ * @throws Error('INVALID_TWO_FACTOR_CODE') on mismatch.
53
+ */
54
+ export declare function verifyTwoFactor(params: {
55
+ userId: string;
56
+ code: string;
57
+ db: DatabaseAdapter;
58
+ }): Promise<User>;
59
+ /**
60
+ * Use a single-use recovery code instead of a TOTP code. The matching token
61
+ * row is deleted before this function returns so a replay fails.
62
+ *
63
+ * @throws Error('USER_NOT_FOUND') if the user does not exist.
64
+ * @throws Error('TWO_FACTOR_NOT_ENABLED') if 2FA is not enrolled for the user.
65
+ * @throws Error('INVALID_RECOVERY_CODE') if the code is unknown.
66
+ */
67
+ export declare function useRecoveryCode(params: {
68
+ userId: string;
69
+ rawCode: string;
70
+ db: DatabaseAdapter;
71
+ }): Promise<User>;
72
+ //# sourceMappingURL=twoFactor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twoFactor.d.ts","sourceRoot":"","sources":["../../src/features/twoFactor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAS5D;;;;;;;;;;;GAWG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE;IAC3C,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,eAAe,CAAA;CACpB,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA0B3E;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,eAAe,CAAA;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhB;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,eAAe,CAAA;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAIhB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,eAAe,CAAA;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAWhB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,EAAE,EAAE,eAAe,CAAA;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBhB"}
@@ -0,0 +1,119 @@
1
+ import { generateTotpSecret, verifyTotpCode, buildOtpauthUrl, generateRecoveryCodes, } from '../utils/totp.js';
2
+ import { hashToken } from '../utils/token.js';
3
+ /**
4
+ * Begin 2FA enrollment for a user.
5
+ *
6
+ * Generates a fresh TOTP secret + a fresh set of recovery codes. The secret is
7
+ * stored on the user immediately so a `setup → enable` sequence on the same
8
+ * device works, but `twoFactorEnabled` stays `false` until `enableTwoFactor`
9
+ * verifies the first code from the authenticator app.
10
+ *
11
+ * Recovery codes are SHA-256 hashed before storage (same pattern as every
12
+ * other AuthCore token). The raw codes are returned ONCE — the caller MUST
13
+ * show them to the user, who copies them down.
14
+ */
15
+ export async function setupTwoFactor(params) {
16
+ const { userId, email, issuer, db } = params;
17
+ const secret = generateTotpSecret();
18
+ const recoveryCodes = generateRecoveryCodes(10);
19
+ // Persist the secret on the user (still disabled). If the user re-runs setup,
20
+ // this overwrites the previous secret — they need to re-scan their authenticator.
21
+ await db.updateUser(userId, { twoFactorSecret: secret });
22
+ // Wipe any prior recovery codes (idempotent re-enrollment).
23
+ await db.deleteTokensByUserAndType(userId, 'RECOVERY_CODE');
24
+ // Persist hashed recovery codes — no expiry, single-use.
25
+ const farFuture = new Date(Date.now() + 100 * 365 * 24 * 60 * 60 * 1000);
26
+ for (const raw of recoveryCodes) {
27
+ await db.createToken({
28
+ userId,
29
+ type: 'RECOVERY_CODE',
30
+ token: hashToken(raw),
31
+ expiresAt: farFuture,
32
+ });
33
+ }
34
+ const otpauthUrl = buildOtpauthUrl({ secret, accountName: email, issuer });
35
+ return { secret, otpauthUrl, recoveryCodes };
36
+ }
37
+ /**
38
+ * Enable 2FA for a user. Requires a valid TOTP code matching the secret stored
39
+ * during {@link setupTwoFactor}. Idempotent against the already-enabled state.
40
+ *
41
+ * @throws Error('TWO_FACTOR_NOT_SET_UP') if setup hasn't run.
42
+ * @throws Error('INVALID_TWO_FACTOR_CODE') if the code doesn't match.
43
+ */
44
+ export async function enableTwoFactor(params) {
45
+ const { userId, code, db } = params;
46
+ const user = await db.findUserById(userId);
47
+ if (!user)
48
+ throw new Error('USER_NOT_FOUND');
49
+ if (!user.twoFactorSecret)
50
+ throw new Error('TWO_FACTOR_NOT_SET_UP');
51
+ if (!verifyTotpCode(user.twoFactorSecret, code)) {
52
+ throw new Error('INVALID_TWO_FACTOR_CODE');
53
+ }
54
+ await db.updateUser(userId, { twoFactorEnabled: true });
55
+ }
56
+ /**
57
+ * Disable 2FA for a user. Clears the secret + all recovery codes + the enabled flag.
58
+ *
59
+ * Callers are responsible for additional confirmation (e.g. password re-entry) —
60
+ * this function trusts that the caller has already authorized the action.
61
+ */
62
+ export async function disableTwoFactor(params) {
63
+ const { userId, db } = params;
64
+ await db.updateUser(userId, { twoFactorEnabled: false, twoFactorSecret: null });
65
+ await db.deleteTokensByUserAndType(userId, 'RECOVERY_CODE');
66
+ }
67
+ /**
68
+ * Verify a TOTP code as the second factor in a login challenge.
69
+ * Returns the user record on success.
70
+ *
71
+ * @throws Error('USER_NOT_FOUND') if no user with that id.
72
+ * @throws Error('TWO_FACTOR_NOT_ENABLED') if the user has not enrolled.
73
+ * @throws Error('INVALID_TWO_FACTOR_CODE') on mismatch.
74
+ */
75
+ export async function verifyTwoFactor(params) {
76
+ const { userId, code, db } = params;
77
+ const user = await db.findUserById(userId);
78
+ if (!user)
79
+ throw new Error('USER_NOT_FOUND');
80
+ if (!user.twoFactorEnabled || !user.twoFactorSecret) {
81
+ throw new Error('TWO_FACTOR_NOT_ENABLED');
82
+ }
83
+ if (!verifyTotpCode(user.twoFactorSecret, code)) {
84
+ throw new Error('INVALID_TWO_FACTOR_CODE');
85
+ }
86
+ return user;
87
+ }
88
+ /**
89
+ * Use a single-use recovery code instead of a TOTP code. The matching token
90
+ * row is deleted before this function returns so a replay fails.
91
+ *
92
+ * @throws Error('USER_NOT_FOUND') if the user does not exist.
93
+ * @throws Error('TWO_FACTOR_NOT_ENABLED') if 2FA is not enrolled for the user.
94
+ * @throws Error('INVALID_RECOVERY_CODE') if the code is unknown.
95
+ */
96
+ export async function useRecoveryCode(params) {
97
+ const { userId, rawCode, db } = params;
98
+ const user = await db.findUserById(userId);
99
+ if (!user)
100
+ throw new Error('USER_NOT_FOUND');
101
+ if (!user.twoFactorEnabled)
102
+ throw new Error('TWO_FACTOR_NOT_ENABLED');
103
+ const tokenRecord = await db.findToken(rawCode, 'RECOVERY_CODE');
104
+ // The token must (1) exist, (2) belong to this user, (3) be non-expired.
105
+ // Recovery codes are stored with a far-future expiry so the second check is
106
+ // belt-and-suspenders — but verifying user ownership prevents an attacker
107
+ // who knows one user's recovery code from authenticating as someone else.
108
+ if (!tokenRecord || tokenRecord.userId !== userId) {
109
+ throw new Error('INVALID_RECOVERY_CODE');
110
+ }
111
+ if (tokenRecord.expiresAt < new Date()) {
112
+ await db.deleteToken(tokenRecord.id);
113
+ throw new Error('INVALID_RECOVERY_CODE');
114
+ }
115
+ // Single-use: delete first so a concurrent replay fails.
116
+ await db.deleteToken(tokenRecord.id);
117
+ return user;
118
+ }
119
+ //# sourceMappingURL=twoFactor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twoFactor.js","sourceRoot":"","sources":["../../src/features/twoFactor.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,14 +1,28 @@
1
- export type { User, Token, TokenType, CreateUserInput, CreateTokenInput, PublicUser, SessionConfig, EmailConfig, AuthCallbacks, AuthCoreConfig, } from './types.js';
2
- export type { DatabaseAdapter } from './adapters/database.interface.js';
3
- export type { EmailAdapter } from './adapters/email.interface.js';
4
1
  export { hashPassword, verifyPassword } from './utils/password.js';
5
- export { generateOpaqueToken, hashToken, safeCompareTokens, signJwt, verifyJwt, } from './utils/token.js';
6
- export type { JwtPayload } from './utils/token.js';
7
- export { registerSchema, loginSchema, forgotPasswordSchema, resetPasswordSchema, verifyEmailSchema, inviteSchema, acceptInvitationSchema, } from './utils/validation.js';
8
- export type { RegisterInput, LoginInput, ForgotPasswordInput, ResetPasswordInput, VerifyEmailInput, InviteInput, AcceptInvitationInput, } from './utils/validation.js';
2
+ export { generateOpaqueToken, generateCsrfToken, generatePkceVerifier, pkceChallenge, hashToken, safeCompareTokens, signJwt, verifyJwt, signTwoFactorChallenge, verifyTwoFactorChallenge, } from './utils/token.js';
3
+ export type { JwtPayload, TwoFactorChallengePayload } from './utils/token.js';
4
+ export { base32Encode, base32Decode, generateTotpSecret, generateTotpCode, verifyTotpCode, buildOtpauthUrl, generateRecoveryCodes, } from './utils/totp.js';
5
+ export { registerSchema, loginSchema, forgotPasswordSchema, resetPasswordSchema, verifyEmailSchema, inviteSchema, acceptInvitationSchema, sendMagicLinkSchema, consumeMagicLinkSchema, } from './utils/validation.js';
6
+ export type { RegisterInput, LoginInput, ForgotPasswordInput, ResetPasswordInput, VerifyEmailInput, InviteInput, AcceptInvitationInput, SendMagicLinkInput, ConsumeMagicLinkInput, } from './utils/validation.js';
9
7
  export { createEmailVerification, verifyEmail } from './features/emailVerification.js';
10
8
  export { createPasswordReset, resetPassword } from './features/passwordReset.js';
11
9
  export { createInvitation, acceptInvitation } from './features/invitation.js';
10
+ export { issueRefreshToken, rotateRefreshToken, revokeRefreshToken, revokeAllRefreshTokensForUser, } from './features/refresh.js';
11
+ export { defaultVerifyEmailTemplate, defaultResetPasswordTemplate, defaultInvitationTemplate, defaultMagicLinkTemplate, } from './features/templates.js';
12
+ export { sendMagicLink, consumeMagicLink, MAGIC_LINK_NO_PASSWORD_SENTINEL, } from './features/magicLink.js';
13
+ export { setupTwoFactor, enableTwoFactor, disableTwoFactor, verifyTwoFactor, useRecoveryCode, } from './features/twoFactor.js';
14
+ export { startOAuth, completeOAuth } from './features/oauth.js';
15
+ export { createGoogleProvider } from './oauth/google.js';
16
+ export type { GoogleProviderConfig } from './oauth/google.js';
17
+ export { createGithubProvider } from './oauth/github.js';
18
+ export type { GithubProviderConfig } from './oauth/github.js';
19
+ export { createMicrosoftProvider } from './oauth/microsoft.js';
20
+ export type { MicrosoftProviderConfig } from './oauth/microsoft.js';
21
+ export { createDiscordProvider } from './oauth/discord.js';
22
+ export type { DiscordProviderConfig } from './oauth/discord.js';
23
+ export { createAppleProvider, generateAppleClientSecret } from './oauth/apple.js';
24
+ export type { AppleProviderConfig } from './oauth/apple.js';
12
25
  export { createAuth, AuthError } from './auth.js';
13
- export type { AuthCore } from './auth.js';
26
+ export type { AuthCore, SessionResult, TwoFactorChallengeResult, LoginResult, } from './auth.js';
27
+ export * from '@authcore/types';
14
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,IAAI,EACJ,KAAK,EACL,SAAS,EACT,eAAe,EACf,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,WAAW,EACX,aAAa,EACb,cAAc,GACf,MAAM,YAAY,CAAA;AAGnB,YAAY,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAA;AACvE,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AAGjE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EACL,mBAAmB,EACnB,SAAS,EACT,iBAAiB,EACjB,OAAO,EACP,SAAS,GACV,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EACL,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,aAAa,EACb,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,GACtB,MAAM,uBAAuB,CAAA;AAG9B,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAA;AACtF,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAG7E,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACjD,YAAY,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,OAAO,EACP,SAAS,EACT,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,UAAU,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAC7E,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,qBAAqB,GACtB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,aAAa,EACb,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,qBAAqB,EACrB,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,uBAAuB,CAAA;AAG9B,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAA;AACtF,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC7E,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,0BAA0B,EAC1B,4BAA4B,EAC5B,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,+BAA+B,GAChC,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACxD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,YAAY,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC/D,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AACjF,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAG3D,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AACjD,YAAY,EACV,QAAQ,EACR,aAAa,EACb,wBAAwB,EACxB,WAAW,GACZ,MAAM,WAAW,CAAA;AAElB,cAAc,iBAAiB,CAAA"}
package/dist/index.js CHANGED
@@ -1,11 +1,23 @@
1
1
  // Utilities
2
2
  export { hashPassword, verifyPassword } from './utils/password.js';
3
- export { generateOpaqueToken, hashToken, safeCompareTokens, signJwt, verifyJwt, } from './utils/token.js';
4
- export { registerSchema, loginSchema, forgotPasswordSchema, resetPasswordSchema, verifyEmailSchema, inviteSchema, acceptInvitationSchema, } from './utils/validation.js';
3
+ export { generateOpaqueToken, generateCsrfToken, generatePkceVerifier, pkceChallenge, hashToken, safeCompareTokens, signJwt, verifyJwt, signTwoFactorChallenge, verifyTwoFactorChallenge, } from './utils/token.js';
4
+ export { base32Encode, base32Decode, generateTotpSecret, generateTotpCode, verifyTotpCode, buildOtpauthUrl, generateRecoveryCodes, } from './utils/totp.js';
5
+ export { registerSchema, loginSchema, forgotPasswordSchema, resetPasswordSchema, verifyEmailSchema, inviteSchema, acceptInvitationSchema, sendMagicLinkSchema, consumeMagicLinkSchema, } from './utils/validation.js';
5
6
  // Features
6
7
  export { createEmailVerification, verifyEmail } from './features/emailVerification.js';
7
8
  export { createPasswordReset, resetPassword } from './features/passwordReset.js';
8
9
  export { createInvitation, acceptInvitation } from './features/invitation.js';
10
+ export { issueRefreshToken, rotateRefreshToken, revokeRefreshToken, revokeAllRefreshTokensForUser, } from './features/refresh.js';
11
+ export { defaultVerifyEmailTemplate, defaultResetPasswordTemplate, defaultInvitationTemplate, defaultMagicLinkTemplate, } from './features/templates.js';
12
+ export { sendMagicLink, consumeMagicLink, MAGIC_LINK_NO_PASSWORD_SENTINEL, } from './features/magicLink.js';
13
+ export { setupTwoFactor, enableTwoFactor, disableTwoFactor, verifyTwoFactor, useRecoveryCode, } from './features/twoFactor.js';
14
+ export { startOAuth, completeOAuth } from './features/oauth.js';
15
+ export { createGoogleProvider } from './oauth/google.js';
16
+ export { createGithubProvider } from './oauth/github.js';
17
+ export { createMicrosoftProvider } from './oauth/microsoft.js';
18
+ export { createDiscordProvider } from './oauth/discord.js';
19
+ export { createAppleProvider, generateAppleClientSecret } from './oauth/apple.js';
9
20
  // Auth factory
10
21
  export { createAuth, AuthError } from './auth.js';
22
+ export * from '@authcore/types';
11
23
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAkBA,YAAY;AACZ,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EACL,mBAAmB,EACnB,SAAS,EACT,iBAAiB,EACjB,OAAO,EACP,SAAS,GACV,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,GACvB,MAAM,uBAAuB,CAAA;AAW9B,WAAW;AACX,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAA;AACtF,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAE7E,eAAe;AACf,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY;AACZ,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,SAAS,EACT,iBAAiB,EACjB,OAAO,EACP,SAAS,EACT,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,qBAAqB,GACtB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,YAAY,EACZ,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,GACvB,MAAM,uBAAuB,CAAA;AAa9B,WAAW;AACX,OAAO,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAA;AACtF,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChF,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAC7E,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,6BAA6B,GAC9B,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,0BAA0B,EAC1B,4BAA4B,EAC5B,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,+BAA+B,GAChC,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,eAAe,GAChB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAExD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AAExD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAE9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAA;AAGjF,eAAe;AACf,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAQjD,cAAc,iBAAiB,CAAA"}
@@ -0,0 +1,80 @@
1
+ import type { OAuthProvider } from '@authcore/types';
2
+ export interface AppleProviderConfig {
3
+ /**
4
+ * Apple Services ID (the OAuth client id), e.g. `com.example.myapp.service`.
5
+ * NOT the iOS app's Bundle ID — Sign in with Apple uses a separate Services ID
6
+ * registered in Apple Developer → Identifiers.
7
+ */
8
+ clientId: string;
9
+ /** Apple Developer Team ID (10-char string, e.g. `ABC1234DEF`). */
10
+ teamId: string;
11
+ /** Sign in with Apple Key ID (10-char string from the .p8 download). */
12
+ keyId: string;
13
+ /**
14
+ * The .p8 private key downloaded from Apple Developer → Keys. Pass the full
15
+ * PEM string including `-----BEGIN PRIVATE KEY-----` / `-----END PRIVATE KEY-----`.
16
+ * Apple delivers the key once at creation time; store it as a secret env var.
17
+ */
18
+ privateKey: string;
19
+ /**
20
+ * Defaults to `['name', 'email']`. Apple only returns `email` in the id_token —
21
+ * `name` is delivered exactly once, on the first sign-in, via a posted form
22
+ * field. AuthCore uses query response_mode and reads identity from the
23
+ * id_token, so the `name` scope is effectively no-op here.
24
+ */
25
+ scopes?: string[];
26
+ /**
27
+ * Client secret JWT TTL in seconds. Defaults to 600 (10 minutes). Apple's
28
+ * maximum is 6 months (15777000s); shorter is safer — we mint a fresh JWT
29
+ * on every exchange anyway.
30
+ */
31
+ clientSecretTtlSeconds?: number;
32
+ }
33
+ /**
34
+ * Create a Sign in with Apple OAuth 2.0 provider.
35
+ *
36
+ * Apple's protocol differs from other providers in one important way: the
37
+ * `client_secret` sent to the token endpoint is **not** a static string. It's
38
+ * an ES256-signed JWT minted on each exchange, signed with a `.p8` private
39
+ * key from Apple Developer. AuthCore handles that for you.
40
+ *
41
+ * ```ts
42
+ * import { createAppleProvider } from '@authcore/core'
43
+ * const apple = createAppleProvider({
44
+ * clientId: 'com.example.myapp.service', // Apple Services ID
45
+ * teamId: 'ABC1234DEF', // Apple Team ID
46
+ * keyId: 'XYZ9876ABC', // Key ID from the .p8 file
47
+ * privateKey: process.env.APPLE_PRIVATE_KEY!, // contents of the .p8 (PEM)
48
+ * })
49
+ * createAuth({ ..., oauth: { apple } })
50
+ * ```
51
+ *
52
+ * **Apple-specific notes:**
53
+ * - Uses `response_mode=query` so the existing AuthCore callback route (GET)
54
+ * handles the response. (Apple's default `form_post` mode would require
55
+ * urlencoded body parsing on the callback; query mode works equivalently
56
+ * for the data we need.)
57
+ * - Apple delivers the user's name **only on first sign-in** via a posted
58
+ * `user` form field that AuthCore does not consume in query mode. The
59
+ * sign-in still works — AuthCore creates the user with `name` unset; the
60
+ * user can edit their profile later. The email is always present.
61
+ * - Apple's `email_verified` claim may be the string `"true"` or a boolean.
62
+ * AuthCore normalizes both to `true`.
63
+ * - Some Apple users sign in with private relay emails (`*@privaterelay.appleid.com`).
64
+ * These are valid forwarding addresses; treat them as real emails.
65
+ */
66
+ export declare function createAppleProvider(config: AppleProviderConfig): OAuthProvider;
67
+ /**
68
+ * Generate the client secret JWT Apple requires on the token endpoint.
69
+ * Signed with ES256 using the developer's .p8 private key.
70
+ *
71
+ * Claims per https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
72
+ */
73
+ export declare function generateAppleClientSecret(params: {
74
+ teamId: string;
75
+ keyId: string;
76
+ clientId: string;
77
+ privateKey: string;
78
+ ttlSeconds?: number;
79
+ }): string;
80
+ //# sourceMappingURL=apple.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple.d.ts","sourceRoot":"","sources":["../../src/oauth/apple.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAA;IACd,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAA;IACb;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAA;CAChC;AAKD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,aAAa,CAmF9E;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE;IAChD,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG,MAAM,CAyBT"}
@@ -0,0 +1,148 @@
1
+ import { createSign } from 'node:crypto';
2
+ const DEFAULT_SCOPES = ['name', 'email'];
3
+ const APPLE_AUD = 'https://appleid.apple.com';
4
+ /**
5
+ * Create a Sign in with Apple OAuth 2.0 provider.
6
+ *
7
+ * Apple's protocol differs from other providers in one important way: the
8
+ * `client_secret` sent to the token endpoint is **not** a static string. It's
9
+ * an ES256-signed JWT minted on each exchange, signed with a `.p8` private
10
+ * key from Apple Developer. AuthCore handles that for you.
11
+ *
12
+ * ```ts
13
+ * import { createAppleProvider } from '@authcore/core'
14
+ * const apple = createAppleProvider({
15
+ * clientId: 'com.example.myapp.service', // Apple Services ID
16
+ * teamId: 'ABC1234DEF', // Apple Team ID
17
+ * keyId: 'XYZ9876ABC', // Key ID from the .p8 file
18
+ * privateKey: process.env.APPLE_PRIVATE_KEY!, // contents of the .p8 (PEM)
19
+ * })
20
+ * createAuth({ ..., oauth: { apple } })
21
+ * ```
22
+ *
23
+ * **Apple-specific notes:**
24
+ * - Uses `response_mode=query` so the existing AuthCore callback route (GET)
25
+ * handles the response. (Apple's default `form_post` mode would require
26
+ * urlencoded body parsing on the callback; query mode works equivalently
27
+ * for the data we need.)
28
+ * - Apple delivers the user's name **only on first sign-in** via a posted
29
+ * `user` form field that AuthCore does not consume in query mode. The
30
+ * sign-in still works — AuthCore creates the user with `name` unset; the
31
+ * user can edit their profile later. The email is always present.
32
+ * - Apple's `email_verified` claim may be the string `"true"` or a boolean.
33
+ * AuthCore normalizes both to `true`.
34
+ * - Some Apple users sign in with private relay emails (`*@privaterelay.appleid.com`).
35
+ * These are valid forwarding addresses; treat them as real emails.
36
+ */
37
+ export function createAppleProvider(config) {
38
+ const scopes = config.scopes ?? DEFAULT_SCOPES;
39
+ const clientSecretTtl = config.clientSecretTtlSeconds ?? 600;
40
+ return {
41
+ id: 'apple',
42
+ scopes,
43
+ authorize: ({ state, codeChallenge, redirectUri }) => {
44
+ const url = new URL('https://appleid.apple.com/auth/authorize');
45
+ url.searchParams.set('client_id', config.clientId);
46
+ url.searchParams.set('redirect_uri', redirectUri);
47
+ url.searchParams.set('response_type', 'code');
48
+ // Apple defaults to form_post; we use query to land on the existing
49
+ // GET callback route. Apple supports both.
50
+ url.searchParams.set('response_mode', 'query');
51
+ url.searchParams.set('scope', scopes.join(' '));
52
+ url.searchParams.set('state', state);
53
+ url.searchParams.set('code_challenge', codeChallenge);
54
+ url.searchParams.set('code_challenge_method', 'S256');
55
+ return url.toString();
56
+ },
57
+ exchangeCode: async ({ code, codeVerifier, redirectUri }) => {
58
+ const clientSecret = generateAppleClientSecret({
59
+ teamId: config.teamId,
60
+ keyId: config.keyId,
61
+ clientId: config.clientId,
62
+ privateKey: config.privateKey,
63
+ ttlSeconds: clientSecretTtl,
64
+ });
65
+ const res = await fetch('https://appleid.apple.com/auth/token', {
66
+ method: 'POST',
67
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
68
+ body: new URLSearchParams({
69
+ code,
70
+ client_id: config.clientId,
71
+ client_secret: clientSecret,
72
+ redirect_uri: redirectUri,
73
+ grant_type: 'authorization_code',
74
+ code_verifier: codeVerifier,
75
+ }),
76
+ });
77
+ if (!res.ok) {
78
+ throw new Error(`Apple token exchange failed (${res.status}): ${await res.text()}`);
79
+ }
80
+ const body = (await res.json());
81
+ return {
82
+ accessToken: body.access_token,
83
+ ...(body.refresh_token !== undefined ? { refreshToken: body.refresh_token } : {}),
84
+ ...(body.expires_in !== undefined ? { expiresIn: body.expires_in } : {}),
85
+ ...(body.id_token !== undefined ? { idToken: body.id_token } : {}),
86
+ };
87
+ },
88
+ getUserInfo: async (_accessToken, idToken) => {
89
+ if (!idToken) {
90
+ throw new Error('Apple OAuth: id_token missing from token response');
91
+ }
92
+ const claims = decodeIdTokenClaims(idToken);
93
+ if (!claims || typeof claims['sub'] !== 'string' || typeof claims['email'] !== 'string') {
94
+ throw new Error('Apple OAuth: id_token missing required claims (sub, email)');
95
+ }
96
+ // Apple sends "true" / "false" as STRINGS in the id_token; some versions
97
+ // return booleans. Normalize.
98
+ const rawVerified = claims['email_verified'];
99
+ const emailVerified = rawVerified === true || rawVerified === 'true' ? true : false;
100
+ return {
101
+ id: claims['sub'],
102
+ email: claims['email'],
103
+ emailVerified,
104
+ };
105
+ },
106
+ };
107
+ }
108
+ /**
109
+ * Generate the client secret JWT Apple requires on the token endpoint.
110
+ * Signed with ES256 using the developer's .p8 private key.
111
+ *
112
+ * Claims per https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens
113
+ */
114
+ export function generateAppleClientSecret(params) {
115
+ const { teamId, keyId, clientId, privateKey, ttlSeconds = 600 } = params;
116
+ const now = Math.floor(Date.now() / 1000);
117
+ const header = { alg: 'ES256', kid: keyId, typ: 'JWT' };
118
+ const payload = {
119
+ iss: teamId,
120
+ iat: now,
121
+ exp: now + ttlSeconds,
122
+ aud: APPLE_AUD,
123
+ sub: clientId,
124
+ };
125
+ const headerB64 = Buffer.from(JSON.stringify(header)).toString('base64url');
126
+ const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url');
127
+ const signingInput = `${headerB64}.${payloadB64}`;
128
+ // Node's signer outputs DER-encoded ECDSA signatures; JWS requires raw R||S.
129
+ // We use `dsaEncoding: 'ieee-p1363'` (Node 16.4+) which produces the raw form.
130
+ const signature = createSign('SHA256')
131
+ .update(signingInput)
132
+ .sign({ key: privateKey, dsaEncoding: 'ieee-p1363' });
133
+ const sigB64 = Buffer.from(signature).toString('base64url');
134
+ return `${signingInput}.${sigB64}`;
135
+ }
136
+ function decodeIdTokenClaims(idToken) {
137
+ const parts = idToken.split('.');
138
+ if (parts.length < 2)
139
+ return null;
140
+ try {
141
+ const payload = Buffer.from(parts[1], 'base64url').toString('utf8');
142
+ return JSON.parse(payload);
143
+ }
144
+ catch {
145
+ return null;
146
+ }
147
+ }
148
+ //# sourceMappingURL=apple.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple.js","sourceRoot":"","sources":["../../src/oauth/apple.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { OAuthProvider } from '@authcore/types';
2
+ export interface DiscordProviderConfig {
3
+ clientId: string;
4
+ clientSecret: string;
5
+ /**
6
+ * Defaults to `['identify', 'email']`. `identify` returns the basic profile;
7
+ * `email` is required to get the user's email + verification status.
8
+ */
9
+ scopes?: string[];
10
+ }
11
+ /**
12
+ * Create a Discord OAuth 2.0 provider.
13
+ *
14
+ * ```ts
15
+ * import { createDiscordProvider } from '@authcore/core'
16
+ * const discord = createDiscordProvider({
17
+ * clientId: process.env.DISCORD_CLIENT_ID!,
18
+ * clientSecret: process.env.DISCORD_CLIENT_SECRET!,
19
+ * })
20
+ * createAuth({ ..., oauth: { discord } })
21
+ * ```
22
+ *
23
+ * Notes:
24
+ * - Discord exposes `verified` on the user object — AuthCore threads that
25
+ * straight through to `emailVerified`. Unverified Discord users will hit
26
+ * the standard AuthCore "EMAIL_NOT_VERIFIED_BY_PROVIDER" gate when linking
27
+ * to an existing local account.
28
+ * - User avatar URLs are constructed from the user's `id` + `avatar` hash
29
+ * (https://discord.com/developers/docs/reference#image-formatting).
30
+ */
31
+ export declare function createDiscordProvider(config: DiscordProviderConfig): OAuthProvider;
32
+ //# sourceMappingURL=discord.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord.d.ts","sourceRoot":"","sources":["../../src/oauth/discord.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;CAClB;AAID;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,aAAa,CAgFlF"}