@soapjs/soap-auth 0.3.2 → 0.4.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 (77) hide show
  1. package/.claude/settings.local.json +20 -0
  2. package/build/__tests__/soap-auth.test.js +94 -0
  3. package/build/errors.d.ts +1 -1
  4. package/build/errors.js +2 -2
  5. package/build/index.d.ts +1 -0
  6. package/build/index.js +1 -0
  7. package/build/services/__tests__/password.service.test.js +25 -18
  8. package/build/services/__tests__/totp.service.test.d.ts +1 -0
  9. package/build/services/__tests__/totp.service.test.js +120 -0
  10. package/build/services/auth-throttle.service.d.ts +2 -2
  11. package/build/services/auth-throttle.service.js +2 -2
  12. package/build/services/index.d.ts +1 -0
  13. package/build/services/index.js +1 -0
  14. package/build/services/password.service.d.ts +7 -5
  15. package/build/services/password.service.js +76 -18
  16. package/build/services/totp.service.d.ts +16 -0
  17. package/build/services/totp.service.js +96 -0
  18. package/build/session/__tests__/session-handler.test.js +22 -14
  19. package/build/session/session-handler.d.ts +1 -0
  20. package/build/session/session-handler.js +39 -6
  21. package/build/soap-auth.d.ts +13 -6
  22. package/build/soap-auth.js +132 -5
  23. package/build/strategies/__tests__/base-auth.strategy.test.d.ts +3 -2
  24. package/build/strategies/__tests__/base-auth.strategy.test.js +1 -0
  25. package/build/strategies/__tests__/credential-auth.strategy.test.d.ts +1 -0
  26. package/build/strategies/__tests__/credential-auth.strategy.test.js +18 -17
  27. package/build/strategies/__tests__/token-auth.strategy.test.d.ts +1 -0
  28. package/build/strategies/__tests__/token-auth.strategy.test.js +1 -0
  29. package/build/strategies/api-key/api-key.strategy.d.ts +4 -4
  30. package/build/strategies/api-key/api-key.strategy.js +3 -2
  31. package/build/strategies/base-auth.strategy.d.ts +5 -4
  32. package/build/strategies/basic/basic.strategy.d.ts +2 -1
  33. package/build/strategies/basic/basic.strategy.js +1 -0
  34. package/build/strategies/credential-auth.strategy.d.ts +7 -7
  35. package/build/strategies/credential-auth.strategy.js +9 -14
  36. package/build/strategies/index.d.ts +1 -0
  37. package/build/strategies/index.js +1 -0
  38. package/build/strategies/jwt/__tests__/jwt.strategy.test.js +2 -2
  39. package/build/strategies/jwt/jwt.strategy.d.ts +3 -1
  40. package/build/strategies/jwt/jwt.strategy.js +35 -6
  41. package/build/strategies/jwt/jwt.tools.js +8 -8
  42. package/build/strategies/local/__tests__/local.strategy.test.js +8 -2
  43. package/build/strategies/local/local.strategy.d.ts +4 -1
  44. package/build/strategies/local/local.strategy.js +81 -0
  45. package/build/strategies/oauth2/__tests__/oauth2.strategy.test.d.ts +1 -0
  46. package/build/strategies/oauth2/__tests__/oauth2.strategy.test.js +239 -0
  47. package/build/strategies/oauth2/hybrid.oauth2.strategy.d.ts +3 -3
  48. package/build/strategies/oauth2/hybrid.oauth2.strategy.js +1 -6
  49. package/build/strategies/oauth2/oauth2.errors.d.ts +1 -0
  50. package/build/strategies/oauth2/oauth2.errors.js +4 -0
  51. package/build/strategies/oauth2/oauth2.strategy.d.ts +6 -4
  52. package/build/strategies/oauth2/oauth2.strategy.js +114 -46
  53. package/build/strategies/oauth2/oauth2.tools.js +2 -2
  54. package/build/strategies/oauth2/oauth2.types.d.ts +2 -2
  55. package/build/strategies/oauth2/providers/__tests__/social-providers.test.d.ts +1 -0
  56. package/build/strategies/oauth2/providers/__tests__/social-providers.test.js +201 -0
  57. package/build/strategies/oauth2/providers/facebook.strategy.d.ts +11 -0
  58. package/build/strategies/oauth2/providers/facebook.strategy.js +58 -0
  59. package/build/strategies/oauth2/providers/github.strategy.d.ts +11 -0
  60. package/build/strategies/oauth2/providers/github.strategy.js +56 -0
  61. package/build/strategies/oauth2/providers/google.strategy.d.ts +11 -0
  62. package/build/strategies/oauth2/providers/google.strategy.js +52 -0
  63. package/build/strategies/oauth2/providers/http-oauth2.strategy.d.ts +16 -0
  64. package/build/strategies/oauth2/providers/http-oauth2.strategy.js +49 -0
  65. package/build/strategies/oauth2/providers/index.d.ts +5 -0
  66. package/build/strategies/oauth2/providers/index.js +21 -0
  67. package/build/strategies/oauth2/providers/provider.types.d.ts +7 -0
  68. package/build/strategies/oauth2/providers/provider.types.js +2 -0
  69. package/build/strategies/token-auth.strategy.d.ts +4 -4
  70. package/build/strategies/token-auth.strategy.js +2 -3
  71. package/build/tools/tools.js +1 -2
  72. package/build/types.d.ts +31 -32
  73. package/build/utils/__tests__/validation.test.d.ts +1 -0
  74. package/build/utils/__tests__/validation.test.js +181 -0
  75. package/build/utils/validation.d.ts +23 -0
  76. package/build/utils/validation.js +139 -0
  77. package/package.json +8 -7
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const google_strategy_1 = require("../google.strategy");
4
+ const github_strategy_1 = require("../github.strategy");
5
+ const facebook_strategy_1 = require("../facebook.strategy");
6
+ const mockFetch = jest.fn();
7
+ global.fetch = mockFetch;
8
+ const baseConfig = {
9
+ clientId: "client-id",
10
+ clientSecret: "client-secret",
11
+ redirectUri: "https://example.com/callback",
12
+ };
13
+ function makeMockCtx() {
14
+ return {
15
+ req: {
16
+ method: "GET",
17
+ path: "/callback",
18
+ headers: { authorization: "Bearer test-token" },
19
+ query: { code: "auth-code", state: "csrf-state" },
20
+ cookies: {},
21
+ },
22
+ res: {
23
+ status: jest.fn().mockReturnThis(),
24
+ json: jest.fn().mockReturnThis(),
25
+ setHeader: jest.fn().mockReturnThis(),
26
+ cookie: jest.fn().mockReturnThis(),
27
+ redirect: jest.fn(),
28
+ },
29
+ next: jest.fn(),
30
+ };
31
+ }
32
+ function mockFetchOk(body) {
33
+ mockFetch.mockResolvedValueOnce({
34
+ ok: true,
35
+ json: async () => body,
36
+ });
37
+ }
38
+ describe("GoogleStrategy", () => {
39
+ afterEach(() => jest.clearAllMocks());
40
+ it("has name 'google'", () => {
41
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
42
+ expect(strategy.name).toBe("google");
43
+ });
44
+ it("maps Google profile to AuthUser", async () => {
45
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
46
+ const googleProfile = {
47
+ sub: "google-sub-123",
48
+ email: "user@gmail.com",
49
+ name: "Test User",
50
+ picture: "https://example.com/pic.jpg",
51
+ email_verified: true,
52
+ };
53
+ mockFetchOk(googleProfile);
54
+ const user = await strategy.fetchUser("test-access-token");
55
+ expect(user).toMatchObject({
56
+ id: "google-sub-123",
57
+ email: "user@gmail.com",
58
+ username: "Test User",
59
+ picture: "https://example.com/pic.jpg",
60
+ emailVerified: true,
61
+ });
62
+ });
63
+ it("calls validateUser when provided", async () => {
64
+ const validateUser = jest.fn().mockResolvedValue({ id: "custom-id", email: "x@x.com" });
65
+ const strategy = new google_strategy_1.GoogleStrategy({ ...baseConfig, user: { fetchUser: jest.fn(), validateUser } });
66
+ mockFetchOk({ sub: "sub123", email: "raw@gmail.com" });
67
+ const user = await strategy.fetchUser("token");
68
+ expect(validateUser).toHaveBeenCalledWith(expect.objectContaining({ sub: "sub123" }));
69
+ expect(user).toMatchObject({ id: "custom-id" });
70
+ });
71
+ it("returns null when fetch fails", async () => {
72
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
73
+ mockFetch.mockResolvedValueOnce({ ok: false, status: 401 });
74
+ const user = await strategy.fetchUser("bad-token");
75
+ expect(user).toBeNull();
76
+ });
77
+ it("uses openid+email+profile scope by default", () => {
78
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
79
+ expect(strategy.config.scope).toContain("openid");
80
+ });
81
+ it("respects custom scope", () => {
82
+ const strategy = new google_strategy_1.GoogleStrategy({ ...baseConfig, scope: ["openid"] });
83
+ expect(strategy.config.scope).toEqual(["openid"]);
84
+ });
85
+ });
86
+ describe("GitHubStrategy", () => {
87
+ afterEach(() => jest.clearAllMocks());
88
+ it("has name 'github'", () => {
89
+ expect(new github_strategy_1.GitHubStrategy(baseConfig).name).toBe("github");
90
+ });
91
+ it("maps GitHub profile to AuthUser", async () => {
92
+ const strategy = new github_strategy_1.GitHubStrategy(baseConfig);
93
+ mockFetchOk({
94
+ id: 12345,
95
+ login: "octocat",
96
+ email: "octocat@github.com",
97
+ name: "The Octocat",
98
+ avatar_url: "https://github.com/avatar.png",
99
+ });
100
+ const user = await strategy.fetchUser("token");
101
+ expect(user).toMatchObject({
102
+ id: 12345,
103
+ email: "octocat@github.com",
104
+ username: "octocat",
105
+ name: "The Octocat",
106
+ avatarUrl: "https://github.com/avatar.png",
107
+ });
108
+ });
109
+ it("returns null when fetch fails", async () => {
110
+ const strategy = new github_strategy_1.GitHubStrategy(baseConfig);
111
+ mockFetch.mockResolvedValueOnce({ ok: false, status: 403 });
112
+ expect(await strategy.fetchUser("bad")).toBeNull();
113
+ });
114
+ });
115
+ describe("FacebookStrategy", () => {
116
+ afterEach(() => jest.clearAllMocks());
117
+ it("has name 'facebook'", () => {
118
+ expect(new facebook_strategy_1.FacebookStrategy(baseConfig).name).toBe("facebook");
119
+ });
120
+ it("maps Facebook profile to AuthUser", async () => {
121
+ const strategy = new facebook_strategy_1.FacebookStrategy(baseConfig);
122
+ mockFetchOk({
123
+ id: "fb-id-456",
124
+ name: "FB User",
125
+ email: "fb@facebook.com",
126
+ picture: { data: { url: "https://fb.com/pic.jpg" } },
127
+ });
128
+ const user = await strategy.fetchUser("token");
129
+ expect(user).toMatchObject({
130
+ id: "fb-id-456",
131
+ email: "fb@facebook.com",
132
+ username: "FB User",
133
+ picture: "https://fb.com/pic.jpg",
134
+ });
135
+ });
136
+ it("returns null on Facebook API error", async () => {
137
+ const strategy = new facebook_strategy_1.FacebookStrategy(baseConfig);
138
+ mockFetchOk({ error: { message: "Invalid token", code: 190 } });
139
+ expect(await strategy.fetchUser("token")).toBeNull();
140
+ });
141
+ it("returns null when fetch fails", async () => {
142
+ const strategy = new facebook_strategy_1.FacebookStrategy(baseConfig);
143
+ mockFetch.mockResolvedValueOnce({ ok: false, status: 401 });
144
+ expect(await strategy.fetchUser("bad")).toBeNull();
145
+ });
146
+ });
147
+ describe("HttpOAuth2Strategy — HTTP plumbing", () => {
148
+ afterEach(() => jest.clearAllMocks());
149
+ it("extracts access token from Authorization header", async () => {
150
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
151
+ const ctx = makeMockCtx();
152
+ const token = await strategy.extractAccessToken(ctx);
153
+ expect(token).toBe("test-token");
154
+ });
155
+ it("extracts access token from cookie when no header", async () => {
156
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
157
+ const ctx = makeMockCtx();
158
+ ctx.req.headers = {};
159
+ ctx.req.cookies = { access_token: "cookie-token" };
160
+ const token = await strategy.extractAccessToken(ctx);
161
+ expect(token).toBe("cookie-token");
162
+ });
163
+ it("extracts refresh token from cookie", async () => {
164
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
165
+ const ctx = makeMockCtx();
166
+ ctx.req.cookies = { refresh_token: "rt-abc" };
167
+ const rt = await strategy.extractRefreshToken(ctx);
168
+ expect(rt).toBe("rt-abc");
169
+ });
170
+ it("extracts authorization code from query", () => {
171
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
172
+ const ctx = makeMockCtx();
173
+ expect(strategy.extractAuthorizationCode(ctx)).toBe("auth-code");
174
+ });
175
+ it("embeds access token as Authorization header", () => {
176
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
177
+ const ctx = makeMockCtx();
178
+ strategy.embedAccessToken("new-token", ctx);
179
+ expect(ctx.res.setHeader).toHaveBeenCalledWith("Authorization", "Bearer new-token");
180
+ });
181
+ it("embeds refresh token as httpOnly cookie", () => {
182
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
183
+ const ctx = makeMockCtx();
184
+ strategy.embedRefreshToken("rt-new", ctx);
185
+ expect(ctx.res.cookie).toHaveBeenCalledWith("refresh_token", "rt-new", expect.objectContaining({ httpOnly: true, secure: true }));
186
+ });
187
+ it("redirects via res.redirect when available", () => {
188
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
189
+ const ctx = makeMockCtx();
190
+ strategy.redirectUser(ctx, "https://accounts.google.com/auth");
191
+ expect(ctx.res.redirect).toHaveBeenCalledWith("https://accounts.google.com/auth");
192
+ });
193
+ it("falls back to Location header when res.redirect is missing", () => {
194
+ const strategy = new google_strategy_1.GoogleStrategy(baseConfig);
195
+ const ctx = makeMockCtx();
196
+ delete ctx.res.redirect;
197
+ strategy.redirectUser(ctx, "https://accounts.google.com/auth");
198
+ expect(ctx.res.setHeader).toHaveBeenCalledWith("Location", "https://accounts.google.com/auth");
199
+ expect(ctx.res.status).toHaveBeenCalledWith(302);
200
+ });
201
+ });
@@ -0,0 +1,11 @@
1
+ import * as Soap from "@soapjs/soap";
2
+ import { SessionHandler } from "../../../session/session-handler";
3
+ import { JwtStrategy } from "../../jwt/jwt.strategy";
4
+ import { HttpOAuth2Strategy } from "./http-oauth2.strategy";
5
+ import { SocialProviderConfig } from "./provider.types";
6
+ export type FacebookStrategyConfig<TUser extends Soap.AuthUser = Soap.AuthUser> = SocialProviderConfig<TUser>;
7
+ export declare class FacebookStrategy<TUser extends Soap.AuthUser = Soap.AuthUser> extends HttpOAuth2Strategy<TUser> {
8
+ readonly name = "facebook";
9
+ constructor(config: FacebookStrategyConfig<TUser>, session?: SessionHandler, jwt?: JwtStrategy<Soap.HttpContext, TUser>, logger?: Soap.Logger);
10
+ protected fetchUser(accessToken: string): Promise<TUser | null>;
11
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FacebookStrategy = void 0;
4
+ const http_oauth2_strategy_1 = require("./http-oauth2.strategy");
5
+ const FACEBOOK_API_VERSION = "v19.0";
6
+ const FACEBOOK_ENDPOINTS = {
7
+ authorizationUrl: "https://www.facebook.com/dialog/oauth",
8
+ tokenUrl: `https://graph.facebook.com/${FACEBOOK_API_VERSION}/oauth/access_token`,
9
+ userInfoUrl: `https://graph.facebook.com/${FACEBOOK_API_VERSION}/me`,
10
+ revocationUrl: `https://graph.facebook.com/${FACEBOOK_API_VERSION}/me/permissions`,
11
+ };
12
+ const FACEBOOK_FIELDS = "id,name,email,picture";
13
+ class FacebookStrategy extends http_oauth2_strategy_1.HttpOAuth2Strategy {
14
+ name = "facebook";
15
+ constructor(config, session, jwt, logger) {
16
+ super({
17
+ grantType: "authorization_code",
18
+ scope: config.scope ?? ["email", "public_profile"],
19
+ routes: {
20
+ login: { path: "/auth/facebook", method: "GET" },
21
+ callback: { path: "/auth/facebook/callback", method: "GET" },
22
+ logout: { path: "/auth/facebook/logout", method: "POST" },
23
+ ...config.routes,
24
+ },
25
+ ...config,
26
+ endpoints: { ...FACEBOOK_ENDPOINTS, ...config.endpoints },
27
+ }, session, jwt, logger);
28
+ }
29
+ async fetchUser(accessToken) {
30
+ try {
31
+ const url = new URL(FACEBOOK_ENDPOINTS.userInfoUrl);
32
+ url.searchParams.set("fields", FACEBOOK_FIELDS);
33
+ url.searchParams.set("access_token", accessToken);
34
+ const response = await fetch(url.toString());
35
+ if (!response.ok)
36
+ return null;
37
+ const profile = await response.json();
38
+ if (profile.error) {
39
+ this.logger?.error("Facebook API error:", profile.error);
40
+ return null;
41
+ }
42
+ if (this.config.user?.validateUser) {
43
+ return this.config.user.validateUser(profile);
44
+ }
45
+ return {
46
+ id: profile.id,
47
+ email: profile.email,
48
+ username: profile.name,
49
+ picture: profile.picture?.data?.url,
50
+ };
51
+ }
52
+ catch (error) {
53
+ this.logger?.error("FacebookStrategy.fetchUser failed:", error);
54
+ return null;
55
+ }
56
+ }
57
+ }
58
+ exports.FacebookStrategy = FacebookStrategy;
@@ -0,0 +1,11 @@
1
+ import * as Soap from "@soapjs/soap";
2
+ import { SessionHandler } from "../../../session/session-handler";
3
+ import { JwtStrategy } from "../../jwt/jwt.strategy";
4
+ import { HttpOAuth2Strategy } from "./http-oauth2.strategy";
5
+ import { SocialProviderConfig } from "./provider.types";
6
+ export type GitHubStrategyConfig<TUser extends Soap.AuthUser = Soap.AuthUser> = SocialProviderConfig<TUser>;
7
+ export declare class GitHubStrategy<TUser extends Soap.AuthUser = Soap.AuthUser> extends HttpOAuth2Strategy<TUser> {
8
+ readonly name = "github";
9
+ constructor(config: GitHubStrategyConfig<TUser>, session?: SessionHandler, jwt?: JwtStrategy<Soap.HttpContext, TUser>, logger?: Soap.Logger);
10
+ protected fetchUser(accessToken: string): Promise<TUser | null>;
11
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GitHubStrategy = void 0;
4
+ const http_oauth2_strategy_1 = require("./http-oauth2.strategy");
5
+ const GITHUB_ENDPOINTS = {
6
+ authorizationUrl: "https://github.com/login/oauth/authorize",
7
+ tokenUrl: "https://github.com/login/oauth/access_token",
8
+ userInfoUrl: "https://api.github.com/user",
9
+ revocationUrl: "https://api.github.com/applications/{client_id}/token",
10
+ };
11
+ class GitHubStrategy extends http_oauth2_strategy_1.HttpOAuth2Strategy {
12
+ name = "github";
13
+ constructor(config, session, jwt, logger) {
14
+ super({
15
+ grantType: "authorization_code",
16
+ scope: config.scope ?? ["read:user", "user:email"],
17
+ routes: {
18
+ login: { path: "/auth/github", method: "GET" },
19
+ callback: { path: "/auth/github/callback", method: "GET" },
20
+ logout: { path: "/auth/github/logout", method: "POST" },
21
+ ...config.routes,
22
+ },
23
+ ...config,
24
+ endpoints: { ...GITHUB_ENDPOINTS, ...config.endpoints },
25
+ }, session, jwt, logger);
26
+ }
27
+ async fetchUser(accessToken) {
28
+ try {
29
+ const response = await fetch(GITHUB_ENDPOINTS.userInfoUrl, {
30
+ headers: {
31
+ Authorization: `Bearer ${accessToken}`,
32
+ Accept: "application/vnd.github+json",
33
+ "X-GitHub-Api-Version": "2022-11-28",
34
+ },
35
+ });
36
+ if (!response.ok)
37
+ return null;
38
+ const profile = await response.json();
39
+ if (this.config.user?.validateUser) {
40
+ return this.config.user.validateUser(profile);
41
+ }
42
+ return {
43
+ id: profile.id,
44
+ email: profile.email,
45
+ username: profile.login,
46
+ name: profile.name,
47
+ avatarUrl: profile.avatar_url,
48
+ };
49
+ }
50
+ catch (error) {
51
+ this.logger?.error("GitHubStrategy.fetchUser failed:", error);
52
+ return null;
53
+ }
54
+ }
55
+ }
56
+ exports.GitHubStrategy = GitHubStrategy;
@@ -0,0 +1,11 @@
1
+ import * as Soap from "@soapjs/soap";
2
+ import { SessionHandler } from "../../../session/session-handler";
3
+ import { JwtStrategy } from "../../jwt/jwt.strategy";
4
+ import { HttpOAuth2Strategy } from "./http-oauth2.strategy";
5
+ import { SocialProviderConfig } from "./provider.types";
6
+ export type GoogleStrategyConfig<TUser extends Soap.AuthUser = Soap.AuthUser> = SocialProviderConfig<TUser>;
7
+ export declare class GoogleStrategy<TUser extends Soap.AuthUser = Soap.AuthUser> extends HttpOAuth2Strategy<TUser> {
8
+ readonly name = "google";
9
+ constructor(config: GoogleStrategyConfig<TUser>, session?: SessionHandler, jwt?: JwtStrategy<Soap.HttpContext, TUser>, logger?: Soap.Logger);
10
+ protected fetchUser(accessToken: string): Promise<TUser | null>;
11
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GoogleStrategy = void 0;
4
+ const http_oauth2_strategy_1 = require("./http-oauth2.strategy");
5
+ const GOOGLE_ENDPOINTS = {
6
+ authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
7
+ tokenUrl: "https://oauth2.googleapis.com/token",
8
+ userInfoUrl: "https://openidconnect.googleapis.com/v1/userinfo",
9
+ revocationUrl: "https://oauth2.googleapis.com/revoke",
10
+ };
11
+ class GoogleStrategy extends http_oauth2_strategy_1.HttpOAuth2Strategy {
12
+ name = "google";
13
+ constructor(config, session, jwt, logger) {
14
+ super({
15
+ grantType: "authorization_code",
16
+ scope: config.scope ?? ["openid", "email", "profile"],
17
+ routes: {
18
+ login: { path: "/auth/google", method: "GET" },
19
+ callback: { path: "/auth/google/callback", method: "GET" },
20
+ logout: { path: "/auth/google/logout", method: "POST" },
21
+ ...config.routes,
22
+ },
23
+ ...config,
24
+ endpoints: { ...GOOGLE_ENDPOINTS, ...config.endpoints },
25
+ }, session, jwt, logger);
26
+ }
27
+ async fetchUser(accessToken) {
28
+ try {
29
+ const response = await fetch(GOOGLE_ENDPOINTS.userInfoUrl, {
30
+ headers: { Authorization: `Bearer ${accessToken}` },
31
+ });
32
+ if (!response.ok)
33
+ return null;
34
+ const profile = await response.json();
35
+ if (this.config.user?.validateUser) {
36
+ return this.config.user.validateUser(profile);
37
+ }
38
+ return {
39
+ id: profile.sub,
40
+ email: profile.email,
41
+ username: profile.name,
42
+ picture: profile.picture,
43
+ emailVerified: profile.email_verified,
44
+ };
45
+ }
46
+ catch (error) {
47
+ this.logger?.error("GoogleStrategy.fetchUser failed:", error);
48
+ return null;
49
+ }
50
+ }
51
+ }
52
+ exports.GoogleStrategy = GoogleStrategy;
@@ -0,0 +1,16 @@
1
+ import * as Soap from "@soapjs/soap";
2
+ import { OAuth2Strategy } from "../oauth2.strategy";
3
+ import { OAuth2StrategyConfig } from "../oauth2.types";
4
+ import { SessionHandler } from "../../../session/session-handler";
5
+ import { JwtStrategy } from "../../jwt/jwt.strategy";
6
+ export declare abstract class HttpOAuth2Strategy<TUser extends Soap.AuthUser = Soap.AuthUser> extends OAuth2Strategy<Soap.HttpContext, TUser> {
7
+ constructor(config: OAuth2StrategyConfig<Soap.HttpContext, TUser>, session?: SessionHandler, jwt?: JwtStrategy<Soap.HttpContext, TUser>, logger?: Soap.Logger);
8
+ protected extractAccessToken(ctx: Soap.HttpContext): Promise<string | undefined>;
9
+ protected extractRefreshToken(ctx: Soap.HttpContext): Promise<string | undefined>;
10
+ protected storeAccessToken(_token: string, _ctx: Soap.HttpContext): Promise<void>;
11
+ protected storeRefreshToken(_token: string, _ctx: Soap.HttpContext): Promise<void>;
12
+ protected embedAccessToken(token: string, ctx: Soap.HttpContext): void;
13
+ protected embedRefreshToken(token: string, ctx: Soap.HttpContext): void;
14
+ protected extractAuthorizationCode(ctx: Soap.HttpContext): string | null;
15
+ protected redirectUser(ctx: Soap.HttpContext, authUrl: string): void;
16
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpOAuth2Strategy = void 0;
4
+ const oauth2_strategy_1 = require("../oauth2.strategy");
5
+ class HttpOAuth2Strategy extends oauth2_strategy_1.OAuth2Strategy {
6
+ constructor(config, session, jwt, logger) {
7
+ super(config, session, jwt, logger);
8
+ }
9
+ extractAccessToken(ctx) {
10
+ const auth = ctx.req.headers?.authorization;
11
+ if (typeof auth === "string" && auth.startsWith("Bearer ")) {
12
+ return Promise.resolve(auth.slice(7));
13
+ }
14
+ return Promise.resolve(ctx.req.cookies?.access_token);
15
+ }
16
+ extractRefreshToken(ctx) {
17
+ return Promise.resolve(ctx.req.cookies?.refresh_token);
18
+ }
19
+ storeAccessToken(_token, _ctx) {
20
+ return Promise.resolve();
21
+ }
22
+ storeRefreshToken(_token, _ctx) {
23
+ return Promise.resolve();
24
+ }
25
+ embedAccessToken(token, ctx) {
26
+ ctx.res.setHeader("Authorization", `Bearer ${token}`);
27
+ }
28
+ embedRefreshToken(token, ctx) {
29
+ ctx.res.cookie("refresh_token", token, {
30
+ httpOnly: true,
31
+ secure: true,
32
+ sameSite: "lax",
33
+ maxAge: 30 * 24 * 60 * 60 * 1000,
34
+ });
35
+ }
36
+ extractAuthorizationCode(ctx) {
37
+ return ctx.req.query?.code ?? null;
38
+ }
39
+ redirectUser(ctx, authUrl) {
40
+ if (typeof ctx.res.redirect === "function") {
41
+ ctx.res.redirect(authUrl);
42
+ }
43
+ else {
44
+ ctx.res.setHeader("Location", authUrl);
45
+ ctx.res.status(302).json({ message: "Redirecting", location: authUrl });
46
+ }
47
+ }
48
+ }
49
+ exports.HttpOAuth2Strategy = HttpOAuth2Strategy;
@@ -0,0 +1,5 @@
1
+ export * from "./http-oauth2.strategy";
2
+ export * from "./google.strategy";
3
+ export * from "./github.strategy";
4
+ export * from "./facebook.strategy";
5
+ export * from "./provider.types";
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./http-oauth2.strategy"), exports);
18
+ __exportStar(require("./google.strategy"), exports);
19
+ __exportStar(require("./github.strategy"), exports);
20
+ __exportStar(require("./facebook.strategy"), exports);
21
+ __exportStar(require("./provider.types"), exports);
@@ -0,0 +1,7 @@
1
+ import * as Soap from "@soapjs/soap";
2
+ import { OAuth2StrategyConfig, OAuth2Endpoints } from "../oauth2.types";
3
+ export interface SocialProviderConfig<TUser extends Soap.AuthUser = Soap.AuthUser> extends Omit<OAuth2StrategyConfig<Soap.HttpContext, TUser>, "endpoints" | "grantType" | "routes"> {
4
+ grantType?: "authorization_code";
5
+ endpoints?: Partial<OAuth2Endpoints>;
6
+ routes?: OAuth2StrategyConfig<Soap.HttpContext, TUser>["routes"];
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,8 +1,8 @@
1
1
  import * as Soap from "@soapjs/soap";
2
- import { AuthResult, TokenAuthStrategyConfig } from "../types";
2
+ import { TokenAuthStrategyConfig } from "../types";
3
3
  import { BaseAuthStrategy } from "./base-auth.strategy";
4
4
  import { SessionHandler } from "../session/session-handler";
5
- export declare abstract class TokenAuthStrategy<TContext = unknown, TUser = unknown> extends BaseAuthStrategy<TContext, TUser> {
5
+ export declare abstract class TokenAuthStrategy<TContext = Soap.HttpContext, TUser extends Soap.AuthUser = Soap.AuthUser> extends BaseAuthStrategy<TContext, TUser> {
6
6
  protected config: TokenAuthStrategyConfig<TContext, TUser>;
7
7
  protected session?: SessionHandler;
8
8
  protected logger?: Soap.Logger;
@@ -20,9 +20,9 @@ export declare abstract class TokenAuthStrategy<TContext = unknown, TUser = unkn
20
20
  protected abstract embedAccessToken(token: string, context: TContext): void;
21
21
  protected abstract embedRefreshToken(token: string, context: TContext): void;
22
22
  protected fetchUser(payload: unknown): Promise<TUser | null>;
23
- authenticate(context: TContext): Promise<AuthResult<TUser>>;
23
+ authenticate(context: TContext): Promise<Soap.AuthResult<TUser> | null>;
24
24
  private verifyAndFetchUser;
25
- refreshTokens(context: TContext, existingUser?: TUser): Promise<AuthResult<TUser>>;
25
+ refreshTokens(context: TContext, existingUser?: TUser): Promise<Soap.AuthResult<TUser>>;
26
26
  issueTokens(user: TUser, context: TContext, rotate?: boolean): Promise<{
27
27
  accessToken: string;
28
28
  refreshToken: any;
@@ -145,12 +145,11 @@ class TokenAuthStrategy extends base_auth_strategy_1.BaseAuthStrategy {
145
145
  const accessToken = await this.generateAccessToken(user, context);
146
146
  let refreshToken;
147
147
  if (this.config.refreshToken) {
148
- refreshToken = await this.generateRefreshToken(user, context);
149
- if (rotate && this.config?.refreshToken?.rotation) {
148
+ if (rotate && this.config.refreshToken.rotation) {
150
149
  const oldRefreshToken = this.extractRefreshToken(context);
151
150
  refreshToken = await this.rotateToken(oldRefreshToken, user, context);
152
151
  }
153
- else if (this.generateRefreshToken) {
152
+ else {
154
153
  refreshToken = await this.generateRefreshToken(user, context);
155
154
  }
156
155
  }
@@ -4,10 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.generateRandomString = exports.resolveConfig = exports.generateUUID = void 0;
7
- const uuid_1 = require("uuid");
8
7
  const crypto_1 = __importDefault(require("crypto"));
9
8
  const generateUUID = async () => {
10
- return Promise.resolve((0, uuid_1.v4)());
9
+ return Promise.resolve(crypto_1.default.randomUUID());
11
10
  };
12
11
  exports.generateUUID = generateUUID;
13
12
  function resolveConfig(strategyConfig, globalConfig) {