@bactv/socialite 1.0.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 (62) hide show
  1. package/README.md +133 -0
  2. package/dist/application/dto/ProviderConfig.d.ts +23 -0
  3. package/dist/application/dto/ProviderConfig.js +3 -0
  4. package/dist/application/dto/ProviderConfig.js.map +1 -0
  5. package/dist/application/services/SocialiteManager.d.ts +50 -0
  6. package/dist/application/services/SocialiteManager.js +73 -0
  7. package/dist/application/services/SocialiteManager.js.map +1 -0
  8. package/dist/domain/contracts/HttpClientContract.d.ts +12 -0
  9. package/dist/domain/contracts/HttpClientContract.js +3 -0
  10. package/dist/domain/contracts/HttpClientContract.js.map +1 -0
  11. package/dist/domain/contracts/OAuthProviderContract.d.ts +28 -0
  12. package/dist/domain/contracts/OAuthProviderContract.js +3 -0
  13. package/dist/domain/contracts/OAuthProviderContract.js.map +1 -0
  14. package/dist/domain/contracts/StateStoreContract.d.ts +13 -0
  15. package/dist/domain/contracts/StateStoreContract.js +3 -0
  16. package/dist/domain/contracts/StateStoreContract.js.map +1 -0
  17. package/dist/domain/entities/SocialUser.d.ts +36 -0
  18. package/dist/domain/entities/SocialUser.js +33 -0
  19. package/dist/domain/entities/SocialUser.js.map +1 -0
  20. package/dist/domain/errors/SocialiteError.d.ts +18 -0
  21. package/dist/domain/errors/SocialiteError.js +46 -0
  22. package/dist/domain/errors/SocialiteError.js.map +1 -0
  23. package/dist/domain/value-objects/AccessTokenResult.d.ts +16 -0
  24. package/dist/domain/value-objects/AccessTokenResult.js +28 -0
  25. package/dist/domain/value-objects/AccessTokenResult.js.map +1 -0
  26. package/dist/domain/value-objects/ProviderName.d.ts +9 -0
  27. package/dist/domain/value-objects/ProviderName.js +22 -0
  28. package/dist/domain/value-objects/ProviderName.js.map +1 -0
  29. package/dist/index.d.ts +36 -0
  30. package/dist/index.js +56 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/infrastructure/http/FetchHttpClient.d.ts +13 -0
  33. package/dist/infrastructure/http/FetchHttpClient.js +58 -0
  34. package/dist/infrastructure/http/FetchHttpClient.js.map +1 -0
  35. package/dist/infrastructure/http/MemoryStateStore.d.ts +15 -0
  36. package/dist/infrastructure/http/MemoryStateStore.js +29 -0
  37. package/dist/infrastructure/http/MemoryStateStore.js.map +1 -0
  38. package/dist/infrastructure/providers/AbstractProvider.d.ts +39 -0
  39. package/dist/infrastructure/providers/AbstractProvider.js +98 -0
  40. package/dist/infrastructure/providers/AbstractProvider.js.map +1 -0
  41. package/dist/infrastructure/providers/FacebookProvider.d.ts +14 -0
  42. package/dist/infrastructure/providers/FacebookProvider.js +55 -0
  43. package/dist/infrastructure/providers/FacebookProvider.js.map +1 -0
  44. package/dist/infrastructure/providers/GithubProvider.d.ts +12 -0
  45. package/dist/infrastructure/providers/GithubProvider.js +55 -0
  46. package/dist/infrastructure/providers/GithubProvider.js.map +1 -0
  47. package/dist/infrastructure/providers/GoogleProvider.d.ts +11 -0
  48. package/dist/infrastructure/providers/GoogleProvider.js +31 -0
  49. package/dist/infrastructure/providers/GoogleProvider.js.map +1 -0
  50. package/dist/infrastructure/providers/LinkedinProvider.d.ts +11 -0
  51. package/dist/infrastructure/providers/LinkedinProvider.js +33 -0
  52. package/dist/infrastructure/providers/LinkedinProvider.js.map +1 -0
  53. package/dist/infrastructure/providers/MicrosoftProvider.d.ts +11 -0
  54. package/dist/infrastructure/providers/MicrosoftProvider.js +32 -0
  55. package/dist/infrastructure/providers/MicrosoftProvider.js.map +1 -0
  56. package/dist/infrastructure/providers/ProviderFactory.d.ts +15 -0
  57. package/dist/infrastructure/providers/ProviderFactory.js +36 -0
  58. package/dist/infrastructure/providers/ProviderFactory.js.map +1 -0
  59. package/dist/infrastructure/providers/TwitterProvider.d.ts +13 -0
  60. package/dist/infrastructure/providers/TwitterProvider.js +43 -0
  61. package/dist/infrastructure/providers/TwitterProvider.js.map +1 -0
  62. package/package.json +47 -0
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # socialite
2
+
3
+ A Node.js / TypeScript SDK for "Login with ..." (OAuth2) flows, built with
4
+ Domain-Driven Design layering. Supports:
5
+
6
+ - Google
7
+ - Microsoft (Azure AD / personal accounts)
8
+ - Facebook
9
+ - GitHub
10
+ - Twitter / X (OAuth 2.0 + PKCE)
11
+ - LinkedIn (OpenID Connect)
12
+
13
+ ## Why DDD here?
14
+
15
+ Each provider is a slightly different dialect of OAuth2 (different token
16
+ endpoints, some need PKCE, some need GET instead of POST for the token
17
+ exchange, etc). Structuring the SDK in layers keeps that variance contained:
18
+
19
+ ```
20
+ src/
21
+ domain/ <- Pure business rules & contracts (ports). Zero HTTP, zero I/O.
22
+ entities/ SocialUser
23
+ value-objects/ AccessTokenResult, ProviderName
24
+ contracts/ OAuthProviderContract, HttpClientContract, StateStoreContract
25
+ errors/ SocialiteError and subclasses
26
+
27
+ application/ <- Orchestrates domain contracts for the outside world.
28
+ services/ SocialiteManager (the public facade / use-case layer)
29
+ dto/ ProviderConfig, SocialiteConfig
30
+
31
+ infrastructure/ <- Concrete adapters implementing the domain contracts.
32
+ http/ FetchHttpClient, MemoryStateStore
33
+ providers/ AbstractProvider + one class per provider + ProviderFactory
34
+
35
+ index.ts <- Public entry point (Socialite.create(...))
36
+ ```
37
+
38
+ The **application layer only depends on domain contracts**, never on a
39
+ concrete provider class. That means adding a 7th provider (say, Apple) is a
40
+ matter of adding one infrastructure class + one registry entry -- nothing
41
+ in `SocialiteManager` changes (Open/Closed Principle).
42
+
43
+ ## Install
44
+
45
+ ```bash
46
+ npm install socialite
47
+ ```
48
+
49
+ ## Usage
50
+
51
+ ```ts
52
+ import { Socialite } from "@bactv/socialite";
53
+
54
+ const socialite = Socialite.create({
55
+ google: {
56
+ clientId: process.env.GOOGLE_CLIENT_ID!,
57
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
58
+ redirectUri: "https://myapp.com/auth/google/callback",
59
+ },
60
+ github: {
61
+ clientId: process.env.GITHUB_CLIENT_ID!,
62
+ clientSecret: process.env.GITHUB_CLIENT_SECRET!,
63
+ redirectUri: "https://myapp.com/auth/github/callback",
64
+ },
65
+ // ...microsoft, facebook, twitter, linkedin follow the same shape
66
+ });
67
+
68
+ // 1) Send the user to the provider's consent screen.
69
+ app.get("/auth/:provider", (req, res) => {
70
+ const { url, state } = socialite.driver(req.params.provider).redirect();
71
+ res.cookie("oauth_state", state, { httpOnly: true });
72
+ res.redirect(url);
73
+ });
74
+
75
+ // 2) Handle the redirect back from the provider.
76
+ app.get("/auth/:provider/callback", async (req, res) => {
77
+ const { code, state } = req.query;
78
+ if (state !== req.cookies.oauth_state) {
79
+ return res.status(400).send("Invalid state");
80
+ }
81
+ const user = await socialite.driver(req.params.provider).callback(code, state);
82
+ // user.id, user.name, user.email, user.avatar, user.accessToken, ...
83
+ res.json(user.toJSON());
84
+ });
85
+ ```
86
+
87
+ See `examples/express-example.ts` for a full working route setup across all
88
+ six providers.
89
+
90
+ ## Custom scopes / extra params
91
+
92
+ ```ts
93
+ Socialite.create({
94
+ google: {
95
+ clientId: "...",
96
+ clientSecret: "...",
97
+ redirectUri: "...",
98
+ scopes: ["openid", "email", "profile", "https://www.googleapis.com/auth/calendar.readonly"],
99
+ authorizationParams: { access_type: "offline", prompt: "consent" },
100
+ },
101
+ });
102
+ ```
103
+
104
+ ## Swapping infrastructure
105
+
106
+ Both the HTTP client and the CSRF/PKCE state store are injected via
107
+ constructor and implement plain interfaces (`HttpClientContract`,
108
+ `StateStoreContract`), so you can supply your own -- e.g. a Redis-backed
109
+ state store for a multi-instance deployment:
110
+
111
+ ```ts
112
+ import { Socialite, StateStoreContract } from "socialite";
113
+
114
+ class RedisStateStore implements StateStoreContract {
115
+ async put(state: string, codeVerifier: string | null) { /* ... */ }
116
+ async pull(state: string) { /* ... */ return null; }
117
+ }
118
+
119
+ const socialite = Socialite.create(config, undefined, new RedisStateStore());
120
+ ```
121
+
122
+ ## Error handling
123
+
124
+ All failure modes raise a subclass of `SocialiteError`:
125
+ `ProviderNotConfiguredError`, `UnsupportedProviderError`,
126
+ `TokenExchangeError`, `UserFetchError`, `InvalidStateError`.
127
+
128
+ ## Build
129
+
130
+ ```bash
131
+ npm install
132
+ npm run build
133
+ ```
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Application DTO: ProviderConfig
3
+ *
4
+ * Plain data shape supplied by the consumer of the SDK (credentials
5
+ * obtained from each provider's developer console).
6
+ */
7
+ export interface ProviderConfig {
8
+ clientId: string;
9
+ clientSecret: string;
10
+ redirectUri: string;
11
+ /** Override default scopes if needed. */
12
+ scopes?: string[];
13
+ /** Extra query params to merge into the authorization URL. */
14
+ authorizationParams?: Record<string, string>;
15
+ }
16
+ export interface SocialiteConfig {
17
+ google?: ProviderConfig;
18
+ microsoft?: ProviderConfig;
19
+ facebook?: ProviderConfig;
20
+ github?: ProviderConfig;
21
+ twitter?: ProviderConfig;
22
+ linkedin?: ProviderConfig;
23
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=ProviderConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProviderConfig.js","sourceRoot":"","sources":["../../../src/application/dto/ProviderConfig.ts"],"names":[],"mappings":""}
@@ -0,0 +1,50 @@
1
+ import { SocialiteConfig } from "../dto/ProviderConfig";
2
+ import { OAuthProviderContract } from "../../domain/contracts/OAuthProviderContract";
3
+ import { HttpClientContract } from "../../domain/contracts/HttpClientContract";
4
+ import { StateStoreContract } from "../../domain/contracts/StateStoreContract";
5
+ import { ProviderName } from "../../domain/value-objects/ProviderName";
6
+ import { SocialUser } from "../../domain/entities/SocialUser";
7
+ export interface RedirectResult {
8
+ /** URL to send the end-user's browser to. */
9
+ url: string;
10
+ /** Opaque CSRF token; persist it (cookie/session) and pass back into `.callback()`. */
11
+ state: string;
12
+ }
13
+ /**
14
+ * Application Service: SocialiteManager
15
+ *
16
+ * The public facade of the SDK. It orchestrates domain contracts and
17
+ * infrastructure adapters but contains no provider-specific HTTP logic
18
+ * itself -- that all lives behind OAuthProviderContract implementations.
19
+ *
20
+ * Usage:
21
+ * const socialite = Socialite.create({ google: {...}, github: {...} });
22
+ * const { url, state } = socialite.driver("google").redirect();
23
+ * // ...later, in your OAuth callback route:
24
+ * const user = await socialite.driver("google").callback(code, state);
25
+ */
26
+ export declare class SocialiteManager {
27
+ private readonly config;
28
+ private readonly http;
29
+ private readonly stateStore;
30
+ private readonly cache;
31
+ constructor(config: SocialiteConfig, http?: HttpClientContract, stateStore?: StateStoreContract);
32
+ /** Resolve (and memoize) the provider identified by `name`. */
33
+ driver(name: string): ProviderFacade;
34
+ /** List provider names currently configured with credentials. */
35
+ configuredProviders(): ProviderName[];
36
+ }
37
+ /**
38
+ * Thin per-request facade returned by `driver()`, giving callers a small,
39
+ * readable API surface (`redirect()` / `callback()`) instead of exposing
40
+ * the full OAuthProviderContract.
41
+ */
42
+ declare class ProviderFacade {
43
+ private readonly provider;
44
+ constructor(provider: OAuthProviderContract);
45
+ /** Step 1: build the URL to redirect the user to, plus a state token to persist. */
46
+ redirect(): RedirectResult;
47
+ /** Step 2: exchange the callback's `code` (+ the `state` you persisted) for a SocialUser. */
48
+ callback(code: string, state?: string): Promise<SocialUser>;
49
+ }
50
+ export {};
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SocialiteManager = void 0;
7
+ const node_crypto_1 = __importDefault(require("node:crypto"));
8
+ const ProviderName_1 = require("../../domain/value-objects/ProviderName");
9
+ const ProviderFactory_1 = require("../../infrastructure/providers/ProviderFactory");
10
+ const FetchHttpClient_1 = require("../../infrastructure/http/FetchHttpClient");
11
+ const MemoryStateStore_1 = require("../../infrastructure/http/MemoryStateStore");
12
+ const SocialiteError_1 = require("../../domain/errors/SocialiteError");
13
+ /**
14
+ * Application Service: SocialiteManager
15
+ *
16
+ * The public facade of the SDK. It orchestrates domain contracts and
17
+ * infrastructure adapters but contains no provider-specific HTTP logic
18
+ * itself -- that all lives behind OAuthProviderContract implementations.
19
+ *
20
+ * Usage:
21
+ * const socialite = Socialite.create({ google: {...}, github: {...} });
22
+ * const { url, state } = socialite.driver("google").redirect();
23
+ * // ...later, in your OAuth callback route:
24
+ * const user = await socialite.driver("google").callback(code, state);
25
+ */
26
+ class SocialiteManager {
27
+ constructor(config, http = new FetchHttpClient_1.FetchHttpClient(), stateStore = new MemoryStateStore_1.MemoryStateStore()) {
28
+ this.config = config;
29
+ this.http = http;
30
+ this.stateStore = stateStore;
31
+ this.cache = new Map();
32
+ }
33
+ /** Resolve (and memoize) the provider identified by `name`. */
34
+ driver(name) {
35
+ if (!(0, ProviderName_1.isSupportedProvider)(name)) {
36
+ throw new SocialiteError_1.UnsupportedProviderError(name);
37
+ }
38
+ if (!this.cache.has(name)) {
39
+ const providerConfig = this.config[name];
40
+ if (!providerConfig) {
41
+ throw new SocialiteError_1.ProviderNotConfiguredError(name);
42
+ }
43
+ this.cache.set(name, ProviderFactory_1.ProviderFactory.make(name, providerConfig, this.http, this.stateStore));
44
+ }
45
+ return new ProviderFacade(this.cache.get(name));
46
+ }
47
+ /** List provider names currently configured with credentials. */
48
+ configuredProviders() {
49
+ return Object.keys(this.config).filter((key) => Boolean(this.config[key]));
50
+ }
51
+ }
52
+ exports.SocialiteManager = SocialiteManager;
53
+ /**
54
+ * Thin per-request facade returned by `driver()`, giving callers a small,
55
+ * readable API surface (`redirect()` / `callback()`) instead of exposing
56
+ * the full OAuthProviderContract.
57
+ */
58
+ class ProviderFacade {
59
+ constructor(provider) {
60
+ this.provider = provider;
61
+ }
62
+ /** Step 1: build the URL to redirect the user to, plus a state token to persist. */
63
+ redirect() {
64
+ const state = node_crypto_1.default.randomBytes(16).toString("hex");
65
+ const url = this.provider.getAuthorizationUrl(state);
66
+ return { url, state };
67
+ }
68
+ /** Step 2: exchange the callback's `code` (+ the `state` you persisted) for a SocialUser. */
69
+ callback(code, state) {
70
+ return this.provider.user(code, state);
71
+ }
72
+ }
73
+ //# sourceMappingURL=SocialiteManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SocialiteManager.js","sourceRoot":"","sources":["../../../src/application/services/SocialiteManager.ts"],"names":[],"mappings":";;;;;;AAAA,8DAAiC;AAKjC,0EAA4F;AAE5F,oFAAiF;AACjF,+EAA4E;AAC5E,iFAA8E;AAC9E,uEAA0G;AAS1G;;;;;;;;;;;;GAYG;AACH,MAAa,gBAAgB;IAG3B,YACmB,MAAuB,EACvB,OAA2B,IAAI,iCAAe,EAAE,EAChD,aAAiC,IAAI,mCAAgB,EAAE;QAFvD,WAAM,GAAN,MAAM,CAAiB;QACvB,SAAI,GAAJ,IAAI,CAA4C;QAChD,eAAU,GAAV,UAAU,CAA6C;QALzD,UAAK,GAAG,IAAI,GAAG,EAAuC,CAAC;IAMrE,CAAC;IAEJ,+DAA+D;IAC/D,MAAM,CAAC,IAAY;QACjB,IAAI,CAAC,IAAA,kCAAmB,EAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,yCAAwB,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,MAAM,IAAI,2CAA0B,CAAC,IAAI,CAAC,CAAC;YAC7C,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,iCAAe,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAC/F,CAAC;QAED,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,CAAC;IACnD,CAAC;IAED,iEAAiE;IACjE,mBAAmB;QACjB,OAAQ,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAoB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACjG,CAAC;CACF;AA9BD,4CA8BC;AAED;;;;GAIG;AACH,MAAM,cAAc;IAClB,YAA6B,QAA+B;QAA/B,aAAQ,GAAR,QAAQ,CAAuB;IAAG,CAAC;IAEhE,oFAAoF;IACpF,QAAQ;QACN,MAAM,KAAK,GAAG,qBAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACrD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IACxB,CAAC;IAED,6FAA6F;IAC7F,QAAQ,CAAC,IAAY,EAAE,KAAc;QACnC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzC,CAAC;CACF"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Domain Contract (Port): HttpClientContract
3
+ *
4
+ * The domain/application layers should never call `fetch` directly.
5
+ * They depend on this abstraction so the actual HTTP implementation
6
+ * (native fetch, axios, a test double, ...) stays an infrastructure
7
+ * concern and is fully swappable/mockable.
8
+ */
9
+ export interface HttpClientContract {
10
+ postForm<T = Record<string, unknown>>(url: string, form: Record<string, string>, headers?: Record<string, string>): Promise<T>;
11
+ getJson<T = Record<string, unknown>>(url: string, headers?: Record<string, string>): Promise<T>;
12
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=HttpClientContract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HttpClientContract.js","sourceRoot":"","sources":["../../../src/domain/contracts/HttpClientContract.ts"],"names":[],"mappings":""}
@@ -0,0 +1,28 @@
1
+ import { SocialUser } from "../entities/SocialUser";
2
+ /**
3
+ * Domain Contract (Port): OAuthProviderContract
4
+ *
5
+ * Defines the behaviour every social login provider must expose.
6
+ * The application layer only ever depends on this interface, never on
7
+ * a concrete provider class -- that's what lets us swap/add providers
8
+ * (infrastructure) without touching application logic (Dependency
9
+ * Inversion Principle).
10
+ */
11
+ export interface OAuthProviderContract {
12
+ /** Unique key such as "google", "github", etc. */
13
+ readonly name: string;
14
+ /**
15
+ * Build the URL the end-user should be redirected to in order to
16
+ * grant consent. `state` is used for CSRF protection and, for
17
+ * PKCE-based providers, a `codeVerifier` is generated/stored internally.
18
+ */
19
+ getAuthorizationUrl(state: string): string;
20
+ /** Exchange a temporary `code` (from the redirect callback) for tokens. */
21
+ getAccessToken(code: string, state?: string): Promise<import("../value-objects/AccessTokenResult").AccessTokenResult>;
22
+ /** Fetch the raw profile payload using a valid access token. */
23
+ getUserByToken(accessToken: string): Promise<Record<string, unknown>>;
24
+ /** Map a raw provider profile + token result into the domain SocialUser entity. */
25
+ mapToSocialUser(raw: Record<string, unknown>, token: import("../value-objects/AccessTokenResult").AccessTokenResult): SocialUser;
26
+ /** Convenience orchestration: code -> token -> profile -> SocialUser. */
27
+ user(code: string, state?: string): Promise<SocialUser>;
28
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=OAuthProviderContract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OAuthProviderContract.js","sourceRoot":"","sources":["../../../src/domain/contracts/OAuthProviderContract.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Domain Contract (Port): StateStoreContract
3
+ *
4
+ * OAuth "state" (CSRF token) and PKCE "code_verifier" values must be
5
+ * persisted between the redirect request and the callback request.
6
+ * In a real HTTP server this is usually backed by a session or cache;
7
+ * consumers of the SDK can provide their own implementation (e.g. Redis,
8
+ * express-session) instead of the in-memory default.
9
+ */
10
+ export interface StateStoreContract {
11
+ put(state: string, codeVerifier: string | null): Promise<void> | void;
12
+ pull(state: string): Promise<string | null> | string | null;
13
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=StateStoreContract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StateStoreContract.js","sourceRoot":"","sources":["../../../src/domain/contracts/StateStoreContract.ts"],"names":[],"mappings":""}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Domain Entity: SocialUser
3
+ *
4
+ * A provider-agnostic representation of an authenticated user.
5
+ * Every concrete provider maps its own raw profile payload into this shape,
6
+ * so application code never has to deal with provider-specific fields.
7
+ */
8
+ export interface SocialUserProps {
9
+ id: string;
10
+ nickname: string | null;
11
+ name: string | null;
12
+ email: string | null;
13
+ avatar: string | null;
14
+ provider: string;
15
+ accessToken: string;
16
+ refreshToken: string | null;
17
+ expiresIn: number | null;
18
+ /** The untouched payload returned by the provider, kept for edge cases. */
19
+ raw: Record<string, unknown>;
20
+ }
21
+ export declare class SocialUser {
22
+ readonly id: string;
23
+ readonly nickname: string | null;
24
+ readonly name: string | null;
25
+ readonly email: string | null;
26
+ readonly avatar: string | null;
27
+ readonly provider: string;
28
+ readonly accessToken: string;
29
+ readonly refreshToken: string | null;
30
+ readonly expiresIn: number | null;
31
+ readonly raw: Record<string, unknown>;
32
+ constructor(props: SocialUserProps);
33
+ toJSON(): Omit<SocialUserProps, "raw"> & {
34
+ raw: Record<string, unknown>;
35
+ };
36
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SocialUser = void 0;
4
+ class SocialUser {
5
+ constructor(props) {
6
+ this.id = props.id;
7
+ this.nickname = props.nickname;
8
+ this.name = props.name;
9
+ this.email = props.email;
10
+ this.avatar = props.avatar;
11
+ this.provider = props.provider;
12
+ this.accessToken = props.accessToken;
13
+ this.refreshToken = props.refreshToken;
14
+ this.expiresIn = props.expiresIn;
15
+ this.raw = props.raw;
16
+ }
17
+ toJSON() {
18
+ return {
19
+ id: this.id,
20
+ nickname: this.nickname,
21
+ name: this.name,
22
+ email: this.email,
23
+ avatar: this.avatar,
24
+ provider: this.provider,
25
+ accessToken: this.accessToken,
26
+ refreshToken: this.refreshToken,
27
+ expiresIn: this.expiresIn,
28
+ raw: this.raw,
29
+ };
30
+ }
31
+ }
32
+ exports.SocialUser = SocialUser;
33
+ //# sourceMappingURL=SocialUser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SocialUser.js","sourceRoot":"","sources":["../../../src/domain/entities/SocialUser.ts"],"names":[],"mappings":";;;AAqBA,MAAa,UAAU;IAYrB,YAAY,KAAsB;QAChC,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC,EAAE,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACvB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;QACrC,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;QACvC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;QACjC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;IACvB,CAAC;IAED,MAAM;QACJ,OAAO;YACL,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC;IACJ,CAAC;CACF;AAvCD,gCAuCC"}
@@ -0,0 +1,18 @@
1
+ export declare class SocialiteError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class InvalidStateError extends SocialiteError {
5
+ constructor();
6
+ }
7
+ export declare class ProviderNotConfiguredError extends SocialiteError {
8
+ constructor(provider: string);
9
+ }
10
+ export declare class UnsupportedProviderError extends SocialiteError {
11
+ constructor(provider: string);
12
+ }
13
+ export declare class TokenExchangeError extends SocialiteError {
14
+ constructor(provider: string, details: string);
15
+ }
16
+ export declare class UserFetchError extends SocialiteError {
17
+ constructor(provider: string, details: string);
18
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UserFetchError = exports.TokenExchangeError = exports.UnsupportedProviderError = exports.ProviderNotConfiguredError = exports.InvalidStateError = exports.SocialiteError = void 0;
4
+ class SocialiteError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "SocialiteError";
8
+ }
9
+ }
10
+ exports.SocialiteError = SocialiteError;
11
+ class InvalidStateError extends SocialiteError {
12
+ constructor() {
13
+ super("The OAuth 'state' parameter is invalid or has expired. Possible CSRF attempt.");
14
+ this.name = "InvalidStateError";
15
+ }
16
+ }
17
+ exports.InvalidStateError = InvalidStateError;
18
+ class ProviderNotConfiguredError extends SocialiteError {
19
+ constructor(provider) {
20
+ super(`Provider "${provider}" is not configured. Pass its clientId/clientSecret/redirectUri to Socialite.create().`);
21
+ this.name = "ProviderNotConfiguredError";
22
+ }
23
+ }
24
+ exports.ProviderNotConfiguredError = ProviderNotConfiguredError;
25
+ class UnsupportedProviderError extends SocialiteError {
26
+ constructor(provider) {
27
+ super(`Provider "${provider}" is not a supported Socialite driver.`);
28
+ this.name = "UnsupportedProviderError";
29
+ }
30
+ }
31
+ exports.UnsupportedProviderError = UnsupportedProviderError;
32
+ class TokenExchangeError extends SocialiteError {
33
+ constructor(provider, details) {
34
+ super(`Failed to exchange authorization code for an access token with "${provider}": ${details}`);
35
+ this.name = "TokenExchangeError";
36
+ }
37
+ }
38
+ exports.TokenExchangeError = TokenExchangeError;
39
+ class UserFetchError extends SocialiteError {
40
+ constructor(provider, details) {
41
+ super(`Failed to fetch user profile from "${provider}": ${details}`);
42
+ this.name = "UserFetchError";
43
+ }
44
+ }
45
+ exports.UserFetchError = UserFetchError;
46
+ //# sourceMappingURL=SocialiteError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SocialiteError.js","sourceRoot":"","sources":["../../../src/domain/errors/SocialiteError.ts"],"names":[],"mappings":";;;AAAA,MAAa,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AALD,wCAKC;AAED,MAAa,iBAAkB,SAAQ,cAAc;IACnD;QACE,KAAK,CAAC,+EAA+E,CAAC,CAAC;QACvF,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AALD,8CAKC;AAED,MAAa,0BAA2B,SAAQ,cAAc;IAC5D,YAAY,QAAgB;QAC1B,KAAK,CAAC,aAAa,QAAQ,wFAAwF,CAAC,CAAC;QACrH,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;IAC3C,CAAC;CACF;AALD,gEAKC;AAED,MAAa,wBAAyB,SAAQ,cAAc;IAC1D,YAAY,QAAgB;QAC1B,KAAK,CAAC,aAAa,QAAQ,wCAAwC,CAAC,CAAC;QACrE,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AALD,4DAKC;AAED,MAAa,kBAAmB,SAAQ,cAAc;IACpD,YAAY,QAAgB,EAAE,OAAe;QAC3C,KAAK,CAAC,mEAAmE,QAAQ,MAAM,OAAO,EAAE,CAAC,CAAC;QAClG,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AALD,gDAKC;AAED,MAAa,cAAe,SAAQ,cAAc;IAChD,YAAY,QAAgB,EAAE,OAAe;QAC3C,KAAK,CAAC,sCAAsC,QAAQ,MAAM,OAAO,EAAE,CAAC,CAAC;QACrE,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AALD,wCAKC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Value Object: AccessTokenResult
3
+ *
4
+ * Immutable representation of the token endpoint response. Value objects
5
+ * in DDD have no identity of their own -- two instances with the same
6
+ * data are considered equal/interchangeable.
7
+ */
8
+ export declare class AccessTokenResult {
9
+ readonly accessToken: string;
10
+ readonly refreshToken: string | null;
11
+ readonly expiresIn: number | null;
12
+ readonly tokenType: string | null;
13
+ readonly raw: Record<string, unknown>;
14
+ constructor(accessToken: string, refreshToken: string | null, expiresIn: number | null, tokenType: string | null, raw: Record<string, unknown>);
15
+ static fromRaw(raw: Record<string, unknown>): AccessTokenResult;
16
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AccessTokenResult = void 0;
4
+ /**
5
+ * Value Object: AccessTokenResult
6
+ *
7
+ * Immutable representation of the token endpoint response. Value objects
8
+ * in DDD have no identity of their own -- two instances with the same
9
+ * data are considered equal/interchangeable.
10
+ */
11
+ class AccessTokenResult {
12
+ constructor(accessToken, refreshToken, expiresIn, tokenType, raw) {
13
+ this.accessToken = accessToken;
14
+ this.refreshToken = refreshToken;
15
+ this.expiresIn = expiresIn;
16
+ this.tokenType = tokenType;
17
+ this.raw = raw;
18
+ }
19
+ static fromRaw(raw) {
20
+ const accessToken = raw.access_token ?? raw.accessToken;
21
+ if (!accessToken) {
22
+ throw new Error("Token response did not contain an access_token field");
23
+ }
24
+ return new AccessTokenResult(accessToken, raw.refresh_token ?? null, typeof raw.expires_in === "number" ? raw.expires_in : null, raw.token_type ?? null, raw);
25
+ }
26
+ }
27
+ exports.AccessTokenResult = AccessTokenResult;
28
+ //# sourceMappingURL=AccessTokenResult.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessTokenResult.js","sourceRoot":"","sources":["../../../src/domain/value-objects/AccessTokenResult.ts"],"names":[],"mappings":";;;AAAA;;;;;;GAMG;AACH,MAAa,iBAAiB;IAC5B,YACkB,WAAmB,EACnB,YAA2B,EAC3B,SAAwB,EACxB,SAAwB,EACxB,GAA4B;QAJ5B,gBAAW,GAAX,WAAW,CAAQ;QACnB,iBAAY,GAAZ,YAAY,CAAe;QAC3B,cAAS,GAAT,SAAS,CAAe;QACxB,cAAS,GAAT,SAAS,CAAe;QACxB,QAAG,GAAH,GAAG,CAAyB;IAC3C,CAAC;IAEJ,MAAM,CAAC,OAAO,CAAC,GAA4B;QACzC,MAAM,WAAW,GAAI,GAAG,CAAC,YAAuB,IAAK,GAAG,CAAC,WAAsB,CAAC;QAChF,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,IAAI,iBAAiB,CAC1B,WAAW,EACV,GAAG,CAAC,aAAwB,IAAI,IAAI,EACrC,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EACzD,GAAG,CAAC,UAAqB,IAAI,IAAI,EAClC,GAAG,CACJ,CAAC;IACJ,CAAC;CACF;AAtBD,8CAsBC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Value Object: ProviderName
3
+ *
4
+ * Restricts provider identifiers to a known, closed set so that typos
5
+ * ("gogle") fail fast at the type level and at runtime.
6
+ */
7
+ export declare const SUPPORTED_PROVIDERS: readonly ["google", "microsoft", "facebook", "github", "twitter", "linkedin"];
8
+ export type ProviderName = (typeof SUPPORTED_PROVIDERS)[number];
9
+ export declare function isSupportedProvider(value: string): value is ProviderName;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SUPPORTED_PROVIDERS = void 0;
4
+ exports.isSupportedProvider = isSupportedProvider;
5
+ /**
6
+ * Value Object: ProviderName
7
+ *
8
+ * Restricts provider identifiers to a known, closed set so that typos
9
+ * ("gogle") fail fast at the type level and at runtime.
10
+ */
11
+ exports.SUPPORTED_PROVIDERS = [
12
+ "google",
13
+ "microsoft",
14
+ "facebook",
15
+ "github",
16
+ "twitter",
17
+ "linkedin",
18
+ ];
19
+ function isSupportedProvider(value) {
20
+ return exports.SUPPORTED_PROVIDERS.includes(value);
21
+ }
22
+ //# sourceMappingURL=ProviderName.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProviderName.js","sourceRoot":"","sources":["../../../src/domain/value-objects/ProviderName.ts"],"names":[],"mappings":";;;AAiBA,kDAEC;AAnBD;;;;;GAKG;AACU,QAAA,mBAAmB,GAAG;IACjC,QAAQ;IACR,WAAW;IACX,UAAU;IACV,QAAQ;IACR,SAAS;IACT,UAAU;CACF,CAAC;AAIX,SAAgB,mBAAmB,CAAC,KAAa;IAC/C,OAAQ,2BAAyC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AACpE,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { SocialiteConfig } from "./application/dto/ProviderConfig";
2
+ import { SocialiteManager } from "./application/services/SocialiteManager";
3
+ import { HttpClientContract } from "./domain/contracts/HttpClientContract";
4
+ import { StateStoreContract } from "./domain/contracts/StateStoreContract";
5
+ /**
6
+ * Socialite -- public factory.
7
+ *
8
+ * const socialite = Socialite.create({
9
+ * google: { clientId, clientSecret, redirectUri },
10
+ * github: { clientId, clientSecret, redirectUri },
11
+ * });
12
+ */
13
+ export declare class Socialite {
14
+ static create(config: SocialiteConfig, http?: HttpClientContract, stateStore?: StateStoreContract): SocialiteManager;
15
+ }
16
+ export { SocialUser } from "./domain/entities/SocialUser";
17
+ export type { SocialUserProps } from "./domain/entities/SocialUser";
18
+ export { AccessTokenResult } from "./domain/value-objects/AccessTokenResult";
19
+ export { SUPPORTED_PROVIDERS, isSupportedProvider } from "./domain/value-objects/ProviderName";
20
+ export type { ProviderName } from "./domain/value-objects/ProviderName";
21
+ export type { OAuthProviderContract } from "./domain/contracts/OAuthProviderContract";
22
+ export type { HttpClientContract } from "./domain/contracts/HttpClientContract";
23
+ export type { StateStoreContract } from "./domain/contracts/StateStoreContract";
24
+ export { SocialiteError, InvalidStateError, ProviderNotConfiguredError, UnsupportedProviderError, TokenExchangeError, UserFetchError, } from "./domain/errors/SocialiteError";
25
+ export { SocialiteManager } from "./application/services/SocialiteManager";
26
+ export type { RedirectResult } from "./application/services/SocialiteManager";
27
+ export type { ProviderConfig, SocialiteConfig } from "./application/dto/ProviderConfig";
28
+ export { FetchHttpClient } from "./infrastructure/http/FetchHttpClient";
29
+ export { MemoryStateStore } from "./infrastructure/http/MemoryStateStore";
30
+ export { ProviderFactory } from "./infrastructure/providers/ProviderFactory";
31
+ export { GoogleProvider } from "./infrastructure/providers/GoogleProvider";
32
+ export { MicrosoftProvider } from "./infrastructure/providers/MicrosoftProvider";
33
+ export { FacebookProvider } from "./infrastructure/providers/FacebookProvider";
34
+ export { GithubProvider } from "./infrastructure/providers/GithubProvider";
35
+ export { TwitterProvider } from "./infrastructure/providers/TwitterProvider";
36
+ export { LinkedinProvider } from "./infrastructure/providers/LinkedinProvider";