@shware/security 0.4.8 → 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 (85) hide show
  1. package/dist/auth/index.cjs +86 -20
  2. package/dist/auth/index.cjs.map +1 -1
  3. package/dist/auth/index.d.cts +8 -2
  4. package/dist/auth/index.d.ts +8 -2
  5. package/dist/auth/index.mjs +87 -21
  6. package/dist/auth/index.mjs.map +1 -1
  7. package/dist/auth/types.cjs.map +1 -1
  8. package/dist/auth/types.d.cts +10 -3
  9. package/dist/auth/types.d.ts +10 -3
  10. package/dist/index.cjs.map +1 -1
  11. package/dist/index.d.cts +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/oauth2/client.cjs +42 -8
  15. package/dist/oauth2/client.cjs.map +1 -1
  16. package/dist/oauth2/client.d.cts +35 -12
  17. package/dist/oauth2/client.d.ts +35 -12
  18. package/dist/oauth2/client.mjs +42 -8
  19. package/dist/oauth2/client.mjs.map +1 -1
  20. package/dist/oauth2/error.cjs.map +1 -1
  21. package/dist/oauth2/error.d.cts +1 -1
  22. package/dist/oauth2/error.d.ts +1 -1
  23. package/dist/oauth2/error.mjs.map +1 -1
  24. package/dist/oauth2/provider/apple.cjs +21 -5
  25. package/dist/oauth2/provider/apple.cjs.map +1 -1
  26. package/dist/oauth2/provider/apple.d.cts +12 -4
  27. package/dist/oauth2/provider/apple.d.ts +12 -4
  28. package/dist/oauth2/provider/apple.mjs +19 -4
  29. package/dist/oauth2/provider/apple.mjs.map +1 -1
  30. package/dist/oauth2/provider/common.cjs +5 -13
  31. package/dist/oauth2/provider/common.cjs.map +1 -1
  32. package/dist/oauth2/provider/common.d.cts +6 -11
  33. package/dist/oauth2/provider/common.d.ts +6 -11
  34. package/dist/oauth2/provider/common.mjs +5 -13
  35. package/dist/oauth2/provider/common.mjs.map +1 -1
  36. package/dist/oauth2/provider/facebook.cjs +19 -3
  37. package/dist/oauth2/provider/facebook.cjs.map +1 -1
  38. package/dist/oauth2/provider/facebook.d.cts +17 -3
  39. package/dist/oauth2/provider/facebook.d.ts +17 -3
  40. package/dist/oauth2/provider/facebook.mjs +18 -3
  41. package/dist/oauth2/provider/facebook.mjs.map +1 -1
  42. package/dist/oauth2/provider/github.cjs +6 -3
  43. package/dist/oauth2/provider/github.cjs.map +1 -1
  44. package/dist/oauth2/provider/github.d.cts +11 -3
  45. package/dist/oauth2/provider/github.d.ts +11 -3
  46. package/dist/oauth2/provider/github.mjs +5 -3
  47. package/dist/oauth2/provider/github.mjs.map +1 -1
  48. package/dist/oauth2/provider/google.cjs +17 -1
  49. package/dist/oauth2/provider/google.cjs.map +1 -1
  50. package/dist/oauth2/provider/google.d.cts +11 -4
  51. package/dist/oauth2/provider/google.d.ts +11 -4
  52. package/dist/oauth2/provider/google.mjs +16 -1
  53. package/dist/oauth2/provider/google.mjs.map +1 -1
  54. package/dist/oauth2/provider/x.cjs +16 -4
  55. package/dist/oauth2/provider/x.cjs.map +1 -1
  56. package/dist/oauth2/provider/x.d.cts +11 -3
  57. package/dist/oauth2/provider/x.d.ts +11 -3
  58. package/dist/oauth2/provider/x.mjs +15 -4
  59. package/dist/oauth2/provider/x.mjs.map +1 -1
  60. package/dist/oauth2/types.cjs +20 -0
  61. package/dist/oauth2/types.cjs.map +1 -1
  62. package/dist/oauth2/types.d.cts +73 -12
  63. package/dist/oauth2/types.d.ts +73 -12
  64. package/dist/oauth2/types.mjs +13 -0
  65. package/dist/oauth2/types.mjs.map +1 -1
  66. package/dist/session/redis-session.cjs +21 -0
  67. package/dist/session/redis-session.cjs.map +1 -1
  68. package/dist/session/redis-session.d.cts +4 -0
  69. package/dist/session/redis-session.d.ts +4 -0
  70. package/dist/session/redis-session.mjs +21 -0
  71. package/dist/session/redis-session.mjs.map +1 -1
  72. package/dist/session/types.cjs.map +1 -1
  73. package/dist/session/types.d.cts +3 -0
  74. package/dist/session/types.d.ts +3 -0
  75. package/dist/utils/http.cjs +2 -2
  76. package/dist/utils/http.cjs.map +1 -1
  77. package/dist/utils/http.mjs +2 -2
  78. package/dist/utils/http.mjs.map +1 -1
  79. package/package.json +1 -1
  80. package/dist/oidc/index.cjs +0 -39
  81. package/dist/oidc/index.cjs.map +0 -1
  82. package/dist/oidc/index.d.cts +0 -10
  83. package/dist/oidc/index.d.ts +0 -10
  84. package/dist/oidc/index.mjs +0 -14
  85. package/dist/oidc/index.mjs.map +0 -1
@@ -3,6 +3,10 @@ var __defProp = Object.defineProperty;
3
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
6
10
  var __copyProps = (to, from, except, desc) => {
7
11
  if (from && typeof from === "object" || typeof from === "function") {
8
12
  for (let key of __getOwnPropNames(from))
@@ -15,5 +19,21 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
15
19
 
16
20
  // src/oauth2/types.ts
17
21
  var types_exports = {};
22
+ __export(types_exports, {
23
+ OidcScopes: () => OidcScopes
24
+ });
18
25
  module.exports = __toCommonJS(types_exports);
26
+ var OidcScopes = /* @__PURE__ */ ((OidcScopes2) => {
27
+ OidcScopes2["openid"] = "openid";
28
+ OidcScopes2["profile"] = "profile";
29
+ OidcScopes2["email"] = "email";
30
+ OidcScopes2["phone"] = "phone";
31
+ OidcScopes2["address"] = "address";
32
+ OidcScopes2["offline_access"] = "offline_access";
33
+ return OidcScopes2;
34
+ })(OidcScopes || {});
35
+ // Annotate the CommonJS export names for ESM import in node:
36
+ 0 && (module.exports = {
37
+ OidcScopes
38
+ });
19
39
  //# sourceMappingURL=types.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/oauth2/types.ts"],"sourcesContent":["export interface OAuth2AuthorizationRequest {\n state: string;\n codeVerifier: string;\n registrationId: string;\n authorizationRequestUri: string;\n}\n\ninterface CreateAuthorizationUriParams {\n state: string;\n clientId: string;\n redirectUri: string;\n scope: string[] | undefined;\n codeVerifier?: string;\n}\n\nexport interface CodeExchangeParams {\n code: string;\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n codeVerifier?: string;\n}\n\nexport interface RefreshTokenParams {\n clientId: string;\n clientSecret: string;\n refreshToken: string;\n}\n\nexport interface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret: string;\n}\n\nexport interface OAuth2Token {\n access_token?: string;\n token_type?: 'Bearer' | 'bearer' | string;\n expires_in?: number;\n refresh_token?: string;\n id_token?: string;\n scope?: string;\n}\n\n// https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\nexport interface StandardClaims {\n sub: string;\n name?: string;\n given_name?: string;\n family_name?: string;\n middle_name?: string;\n nickname?: string;\n preferred_username?: string;\n profile?: string;\n picture?: string;\n website?: string;\n email?: string;\n email_verified?: boolean;\n gender?: 'female' | 'male' | string;\n birthdate?: string; // ISO 8601-1 [ISO8601‑1] YYYY-MM-DD format.\n zoneinfo?: string; // IANA Time Zone Database [IANA.time‑zones]\n locale?: string; // BCP47 [RFC5646] language tag.\n phone_number?: string; // E.164\n phone_number_verified?: boolean;\n address?: {\n formatted: string;\n street_address: string;\n locality: string;\n region: string;\n postal_code: string;\n country: string;\n }; // [RFC8259] JSON object\n updated_at?: number;\n}\n\nexport interface UserInfo<T extends Record<string, any> = Record<string, any>> {\n claims: StandardClaims;\n data: T;\n}\n\nexport interface Provider<T extends Record<string, any> = Record<string, any>> {\n /** Authorization URI for the provider. */\n authorizationUri: string;\n\n /** Token URI for the provider. */\n tokenUri: string;\n\n tokenRevokeUri?: string;\n\n /** User info URI for the provider. */\n userInfoUri?: string;\n\n /** User info authentication method for the provider. */\n userInfoAuthenticationMethod?: string;\n\n /**\n * Name of the attribute that will be used to extract the username from the call to 'userInfoUri'.\n * */\n userNameAttribute?: string;\n\n /** JWK set URI for the provider */\n jwkSetUri?: string;\n\n /**\n * URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0 Authorization\n * Server Metadata endpoint defined by RFC 8414.\n * */\n issuerUri?: string;\n\n /** Default scopes for the provider. */\n defaultScope: string[];\n\n /** step 1: generate authorization uri and redirect user to it */\n createAuthorizationUri: (params: CreateAuthorizationUriParams) => URL;\n\n /** step 2: exchange code for token */\n exchangeAuthorizationCode: (params: CodeExchangeParams) => Promise<OAuth2Token>;\n\n /** step 3: get user info */\n getUserInfo: (token: OAuth2Token) => Promise<UserInfo<T>>;\n\n /** others: refresh access token */\n refreshAccessToken?: (params: RefreshTokenParams) => Promise<OAuth2Token>;\n\n /** others: revoke token */\n revokeToken?: (params: RevokeTokenParams) => Promise<void>;\n}\n\nexport interface Registration {\n /**\n * Reference to the OAuth 2.0 provider to use. May reference an element from the 'provider'\n * property or used one of the commonly used providers (google, github, facebook, okta).\n * */\n provider?: string;\n\n /** Client ID for the registration. */\n clientId: string;\n\n /** Client secret of the registration. */\n clientSecret: string;\n\n /** Client authentication method. May be left blank when using a pre-defined provider. */\n clientAuthenticationMethod?: string;\n\n /** Authorization grant type. May be left blank when using a pre-defined provider. */\n authorizationGrantType?:\n | 'authorization_code' // default\n | 'client_credentials'\n | 'device_code'\n | 'jwt_bearer'\n // Deprecated. The latest OAuth 2.0 Security Best Current Practice disallows the use of the\n // Resource Owner Password Credentials grant.\n | 'password'\n | 'refresh_token'\n | 'token_exchange';\n\n /** Redirect URI. May be left blank when using a pre-defined provider. */\n redirectUri?: string;\n\n /** Authorization scopes. When left blank the provider's default scopes, if any, will be used. */\n scope?: string[];\n\n /** Client name. May be left blank when using a pre-defined provider. */\n clientName?: string;\n}\n\nexport interface OAuth2ClientConfig {\n baseUri: string;\n errorUri: string;\n provider: { [name: string]: Provider | undefined };\n registration: { [name: string]: Registration | undefined };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../../src/oauth2/types.ts"],"sourcesContent":["// reference: https://datatracker.ietf.org/doc/html/rfc7636\nexport interface PkceParameters {\n code_verifier: string;\n code_challenge: string;\n code_challenge_method: 'S256' | 'plain';\n}\n\nexport interface OAuth2AuthorizationRequest {\n state: string;\n registrationId: string;\n authorizationRequestUri: string;\n additionalParameters: PkceParameters;\n}\n\ninterface CreateAuthorizationUriParams {\n state: string;\n clientId: string;\n redirectUri: string;\n scope: string[] | undefined;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}\n\nexport interface ExchangeCodeParams {\n code: string;\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n pkce?: Omit<PkceParameters, 'code_challenge' | 'code_challenge_method'>;\n}\n\nexport interface RefreshTokenParams {\n clientId: string;\n clientSecret: string;\n refreshToken: string;\n}\n\nexport interface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret: string;\n}\n\nexport interface LoginOAuth2NativeParams {\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n pkce?: PkceParameters;\n credentials: NativeCredentials;\n}\n\n// reference: https://datatracker.ietf.org/doc/html/rfc6749#section-5.1\nexport interface OAuth2Token {\n /**\n * REQUIRED.\n * The access token issued by the authorization server.\n */\n access_token: string;\n\n /**\n * REQUIRED.\n * The type of the token issued as described in Section 7.1. Value is case insensitive.\n */\n token_type: 'Bearer' | 'bearer' | 'mac' | (string & {});\n\n /**\n * RECOMMENDED.\n * The lifetime in seconds of the access token. For example, the value \"3600\" denotes that the\n * access token will expire in one hour from the time the response was generated. If omitted, the\n * authorization server SHOULD provide the expiration time via other means or document the default\n * value.\n */\n expires_in?: number;\n\n /**\n * OPTIONAL.\n * The refresh token, which can be used to obtain new access tokens using the same authorization\n * grant as described in Section 6.\n */\n refresh_token?: string;\n\n /**\n * OPTIONAL.\n * If identical to the scope requested by the client; otherwise, REQUIRED. The scope of the\n * access token as described by Section 3.3.\n */\n scope?: string;\n\n id_token?: string;\n}\n\nexport interface OidcToken extends OAuth2Token {\n id_token: string;\n}\n\nexport type NativeCredentials = {\n state: string;\n code?: string;\n client_id?: string;\n redirect_uri?: string;\n id_token?: string;\n access_token?: string;\n};\n\nexport enum OidcScopes {\n openid = 'openid',\n profile = 'profile',\n email = 'email',\n phone = 'phone',\n address = 'address',\n offline_access = 'offline_access',\n}\n\n// https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\nexport interface StandardClaims {\n sub: string;\n name?: string;\n given_name?: string;\n family_name?: string;\n middle_name?: string;\n nickname?: string;\n preferred_username?: string;\n profile?: string;\n picture?: string;\n website?: string;\n email?: string;\n email_verified?: boolean;\n gender?: 'female' | 'male' | string;\n birthdate?: string; // ISO 8601-1 [ISO8601‑1] YYYY-MM-DD format.\n zoneinfo?: string; // IANA Time Zone Database [IANA.time‑zones]\n locale?: string; // BCP47 [RFC5646] language tag.\n phone_number?: string; // E.164\n phone_number_verified?: boolean;\n address?: {\n formatted: string;\n street_address: string;\n locality: string;\n region: string;\n postal_code: string;\n country: string;\n }; // [RFC8259] JSON object\n updated_at?: number;\n}\n\nexport interface UserInfo<T = unknown> {\n claims: StandardClaims;\n data: T;\n}\n\nexport interface Provider {\n /** Authorization URI for the provider. */\n authorizationUri: string;\n\n /** Token URI for the provider. */\n tokenUri: string;\n\n tokenRevokeUri?: string;\n\n /** User info URI for the provider. */\n userInfoUri?: string;\n\n /** User info authentication method for the provider. */\n userInfoAuthenticationMethod?: string;\n\n /**\n * Name of the attribute that will be used to extract the username from the call to 'userInfoUri'.\n * */\n userNameAttribute?: string;\n\n /** JWK set URI for the provider */\n jwkSetUri?: string;\n\n /**\n * URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0 Authorization\n * Server Metadata endpoint defined by RFC 8414.\n * */\n issuerUri?: string;\n\n /** Default scopes for the provider. */\n defaultScope: string[];\n\n /** step 1: generate authorization uri and redirect user to it */\n createAuthorizationUri: (params: CreateAuthorizationUriParams) => URL;\n\n /** step 2: exchange code for token */\n exchangeAuthorizationCode: (params: ExchangeCodeParams) => Promise<OAuth2Token>;\n\n /** step 3: get user info */\n getUserInfo: (token: OAuth2Token) => Promise<UserInfo>;\n\n /** others: refresh access token */\n refreshAccessToken?: (params: RefreshTokenParams) => Promise<OAuth2Token>;\n\n /** others: revoke token */\n revokeToken?: (params: RevokeTokenParams) => Promise<void>;\n\n loginOAuth2Native?: (\n params: LoginOAuth2NativeParams\n ) => Promise<{ token: OAuth2Token; userInfo: UserInfo }>;\n}\n\nexport interface Registration {\n /**\n * Reference to the OAuth 2.0 provider to use. May reference an element from the 'provider'\n * property or used one of the commonly used providers (google, github, facebook, okta).\n * */\n provider?: string;\n\n /** Client ID for the registration. */\n clientId: string;\n\n /** Client secret of the registration. */\n clientSecret: string;\n\n /** Client authentication method. May be left blank when using a pre-defined provider. */\n clientAuthenticationMethod?: string;\n\n /** Authorization grant type. May be left blank when using a pre-defined provider. */\n authorizationGrantType?:\n | 'authorization_code' // default\n | 'client_credentials'\n | 'device_code'\n | 'jwt_bearer'\n // Deprecated. The latest OAuth 2.0 Security Best Current Practice disallows the use of the\n // Resource Owner Password Credentials grant.\n | 'password'\n | 'refresh_token'\n | 'token_exchange';\n\n /** Redirect URI. May be left blank when using a pre-defined provider. */\n redirectUri?: string;\n\n /** Authorization scopes. When left blank the provider's default scopes, if any, will be used. */\n scope?: string[];\n\n /** Client name. May be left blank when using a pre-defined provider. */\n clientName?: string;\n}\n\nexport interface OAuth2ClientConfig {\n baseUri: string;\n errorUri: string;\n successUri: string;\n provider: { [name: string]: Provider | undefined };\n registration: { [name: string]: Registration | undefined };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAuGO,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,WAAQ;AACR,EAAAA,YAAA,WAAQ;AACR,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,oBAAiB;AANP,SAAAA;AAAA,GAAA;","names":["OidcScopes"]}
@@ -1,22 +1,27 @@
1
+ interface PkceParameters {
2
+ code_verifier: string;
3
+ code_challenge: string;
4
+ code_challenge_method: 'S256' | 'plain';
5
+ }
1
6
  interface OAuth2AuthorizationRequest {
2
7
  state: string;
3
- codeVerifier: string;
4
8
  registrationId: string;
5
9
  authorizationRequestUri: string;
10
+ additionalParameters: PkceParameters;
6
11
  }
7
12
  interface CreateAuthorizationUriParams {
8
13
  state: string;
9
14
  clientId: string;
10
15
  redirectUri: string;
11
16
  scope: string[] | undefined;
12
- codeVerifier?: string;
17
+ pkce?: Omit<PkceParameters, 'code_verifier'>;
13
18
  }
14
- interface CodeExchangeParams {
19
+ interface ExchangeCodeParams {
15
20
  code: string;
16
21
  clientId: string;
17
22
  clientSecret: string;
18
23
  redirectUri: string;
19
- codeVerifier?: string;
24
+ pkce?: Omit<PkceParameters, 'code_challenge' | 'code_challenge_method'>;
20
25
  }
21
26
  interface RefreshTokenParams {
22
27
  clientId: string;
@@ -28,13 +33,64 @@ interface RevokeTokenParams {
28
33
  clientId: string;
29
34
  clientSecret: string;
30
35
  }
36
+ interface LoginOAuth2NativeParams {
37
+ clientId: string;
38
+ clientSecret: string;
39
+ redirectUri: string;
40
+ pkce?: PkceParameters;
41
+ credentials: NativeCredentials;
42
+ }
31
43
  interface OAuth2Token {
32
- access_token?: string;
33
- token_type?: 'Bearer' | 'bearer' | string;
44
+ /**
45
+ * REQUIRED.
46
+ * The access token issued by the authorization server.
47
+ */
48
+ access_token: string;
49
+ /**
50
+ * REQUIRED.
51
+ * The type of the token issued as described in Section 7.1. Value is case insensitive.
52
+ */
53
+ token_type: 'Bearer' | 'bearer' | 'mac' | (string & {});
54
+ /**
55
+ * RECOMMENDED.
56
+ * The lifetime in seconds of the access token. For example, the value "3600" denotes that the
57
+ * access token will expire in one hour from the time the response was generated. If omitted, the
58
+ * authorization server SHOULD provide the expiration time via other means or document the default
59
+ * value.
60
+ */
34
61
  expires_in?: number;
62
+ /**
63
+ * OPTIONAL.
64
+ * The refresh token, which can be used to obtain new access tokens using the same authorization
65
+ * grant as described in Section 6.
66
+ */
35
67
  refresh_token?: string;
36
- id_token?: string;
68
+ /**
69
+ * OPTIONAL.
70
+ * If identical to the scope requested by the client; otherwise, REQUIRED. The scope of the
71
+ * access token as described by Section 3.3.
72
+ */
37
73
  scope?: string;
74
+ id_token?: string;
75
+ }
76
+ interface OidcToken extends OAuth2Token {
77
+ id_token: string;
78
+ }
79
+ type NativeCredentials = {
80
+ state: string;
81
+ code?: string;
82
+ client_id?: string;
83
+ redirect_uri?: string;
84
+ id_token?: string;
85
+ access_token?: string;
86
+ };
87
+ declare enum OidcScopes {
88
+ openid = "openid",
89
+ profile = "profile",
90
+ email = "email",
91
+ phone = "phone",
92
+ address = "address",
93
+ offline_access = "offline_access"
38
94
  }
39
95
  interface StandardClaims {
40
96
  sub: string;
@@ -65,11 +121,11 @@ interface StandardClaims {
65
121
  };
66
122
  updated_at?: number;
67
123
  }
68
- interface UserInfo<T extends Record<string, any> = Record<string, any>> {
124
+ interface UserInfo<T = unknown> {
69
125
  claims: StandardClaims;
70
126
  data: T;
71
127
  }
72
- interface Provider<T extends Record<string, any> = Record<string, any>> {
128
+ interface Provider {
73
129
  /** Authorization URI for the provider. */
74
130
  authorizationUri: string;
75
131
  /** Token URI for the provider. */
@@ -95,13 +151,17 @@ interface Provider<T extends Record<string, any> = Record<string, any>> {
95
151
  /** step 1: generate authorization uri and redirect user to it */
96
152
  createAuthorizationUri: (params: CreateAuthorizationUriParams) => URL;
97
153
  /** step 2: exchange code for token */
98
- exchangeAuthorizationCode: (params: CodeExchangeParams) => Promise<OAuth2Token>;
154
+ exchangeAuthorizationCode: (params: ExchangeCodeParams) => Promise<OAuth2Token>;
99
155
  /** step 3: get user info */
100
- getUserInfo: (token: OAuth2Token) => Promise<UserInfo<T>>;
156
+ getUserInfo: (token: OAuth2Token) => Promise<UserInfo>;
101
157
  /** others: refresh access token */
102
158
  refreshAccessToken?: (params: RefreshTokenParams) => Promise<OAuth2Token>;
103
159
  /** others: revoke token */
104
160
  revokeToken?: (params: RevokeTokenParams) => Promise<void>;
161
+ loginOAuth2Native?: (params: LoginOAuth2NativeParams) => Promise<{
162
+ token: OAuth2Token;
163
+ userInfo: UserInfo;
164
+ }>;
105
165
  }
106
166
  interface Registration {
107
167
  /**
@@ -127,6 +187,7 @@ interface Registration {
127
187
  interface OAuth2ClientConfig {
128
188
  baseUri: string;
129
189
  errorUri: string;
190
+ successUri: string;
130
191
  provider: {
131
192
  [name: string]: Provider | undefined;
132
193
  };
@@ -135,4 +196,4 @@ interface OAuth2ClientConfig {
135
196
  };
136
197
  }
137
198
 
138
- export type { CodeExchangeParams, OAuth2AuthorizationRequest, OAuth2ClientConfig, OAuth2Token, Provider, RefreshTokenParams, Registration, RevokeTokenParams, StandardClaims, UserInfo };
199
+ export { type ExchangeCodeParams, type LoginOAuth2NativeParams, type NativeCredentials, type OAuth2AuthorizationRequest, type OAuth2ClientConfig, type OAuth2Token, OidcScopes, type OidcToken, type PkceParameters, type Provider, type RefreshTokenParams, type Registration, type RevokeTokenParams, type StandardClaims, type UserInfo };
@@ -1,22 +1,27 @@
1
+ interface PkceParameters {
2
+ code_verifier: string;
3
+ code_challenge: string;
4
+ code_challenge_method: 'S256' | 'plain';
5
+ }
1
6
  interface OAuth2AuthorizationRequest {
2
7
  state: string;
3
- codeVerifier: string;
4
8
  registrationId: string;
5
9
  authorizationRequestUri: string;
10
+ additionalParameters: PkceParameters;
6
11
  }
7
12
  interface CreateAuthorizationUriParams {
8
13
  state: string;
9
14
  clientId: string;
10
15
  redirectUri: string;
11
16
  scope: string[] | undefined;
12
- codeVerifier?: string;
17
+ pkce?: Omit<PkceParameters, 'code_verifier'>;
13
18
  }
14
- interface CodeExchangeParams {
19
+ interface ExchangeCodeParams {
15
20
  code: string;
16
21
  clientId: string;
17
22
  clientSecret: string;
18
23
  redirectUri: string;
19
- codeVerifier?: string;
24
+ pkce?: Omit<PkceParameters, 'code_challenge' | 'code_challenge_method'>;
20
25
  }
21
26
  interface RefreshTokenParams {
22
27
  clientId: string;
@@ -28,13 +33,64 @@ interface RevokeTokenParams {
28
33
  clientId: string;
29
34
  clientSecret: string;
30
35
  }
36
+ interface LoginOAuth2NativeParams {
37
+ clientId: string;
38
+ clientSecret: string;
39
+ redirectUri: string;
40
+ pkce?: PkceParameters;
41
+ credentials: NativeCredentials;
42
+ }
31
43
  interface OAuth2Token {
32
- access_token?: string;
33
- token_type?: 'Bearer' | 'bearer' | string;
44
+ /**
45
+ * REQUIRED.
46
+ * The access token issued by the authorization server.
47
+ */
48
+ access_token: string;
49
+ /**
50
+ * REQUIRED.
51
+ * The type of the token issued as described in Section 7.1. Value is case insensitive.
52
+ */
53
+ token_type: 'Bearer' | 'bearer' | 'mac' | (string & {});
54
+ /**
55
+ * RECOMMENDED.
56
+ * The lifetime in seconds of the access token. For example, the value "3600" denotes that the
57
+ * access token will expire in one hour from the time the response was generated. If omitted, the
58
+ * authorization server SHOULD provide the expiration time via other means or document the default
59
+ * value.
60
+ */
34
61
  expires_in?: number;
62
+ /**
63
+ * OPTIONAL.
64
+ * The refresh token, which can be used to obtain new access tokens using the same authorization
65
+ * grant as described in Section 6.
66
+ */
35
67
  refresh_token?: string;
36
- id_token?: string;
68
+ /**
69
+ * OPTIONAL.
70
+ * If identical to the scope requested by the client; otherwise, REQUIRED. The scope of the
71
+ * access token as described by Section 3.3.
72
+ */
37
73
  scope?: string;
74
+ id_token?: string;
75
+ }
76
+ interface OidcToken extends OAuth2Token {
77
+ id_token: string;
78
+ }
79
+ type NativeCredentials = {
80
+ state: string;
81
+ code?: string;
82
+ client_id?: string;
83
+ redirect_uri?: string;
84
+ id_token?: string;
85
+ access_token?: string;
86
+ };
87
+ declare enum OidcScopes {
88
+ openid = "openid",
89
+ profile = "profile",
90
+ email = "email",
91
+ phone = "phone",
92
+ address = "address",
93
+ offline_access = "offline_access"
38
94
  }
39
95
  interface StandardClaims {
40
96
  sub: string;
@@ -65,11 +121,11 @@ interface StandardClaims {
65
121
  };
66
122
  updated_at?: number;
67
123
  }
68
- interface UserInfo<T extends Record<string, any> = Record<string, any>> {
124
+ interface UserInfo<T = unknown> {
69
125
  claims: StandardClaims;
70
126
  data: T;
71
127
  }
72
- interface Provider<T extends Record<string, any> = Record<string, any>> {
128
+ interface Provider {
73
129
  /** Authorization URI for the provider. */
74
130
  authorizationUri: string;
75
131
  /** Token URI for the provider. */
@@ -95,13 +151,17 @@ interface Provider<T extends Record<string, any> = Record<string, any>> {
95
151
  /** step 1: generate authorization uri and redirect user to it */
96
152
  createAuthorizationUri: (params: CreateAuthorizationUriParams) => URL;
97
153
  /** step 2: exchange code for token */
98
- exchangeAuthorizationCode: (params: CodeExchangeParams) => Promise<OAuth2Token>;
154
+ exchangeAuthorizationCode: (params: ExchangeCodeParams) => Promise<OAuth2Token>;
99
155
  /** step 3: get user info */
100
- getUserInfo: (token: OAuth2Token) => Promise<UserInfo<T>>;
156
+ getUserInfo: (token: OAuth2Token) => Promise<UserInfo>;
101
157
  /** others: refresh access token */
102
158
  refreshAccessToken?: (params: RefreshTokenParams) => Promise<OAuth2Token>;
103
159
  /** others: revoke token */
104
160
  revokeToken?: (params: RevokeTokenParams) => Promise<void>;
161
+ loginOAuth2Native?: (params: LoginOAuth2NativeParams) => Promise<{
162
+ token: OAuth2Token;
163
+ userInfo: UserInfo;
164
+ }>;
105
165
  }
106
166
  interface Registration {
107
167
  /**
@@ -127,6 +187,7 @@ interface Registration {
127
187
  interface OAuth2ClientConfig {
128
188
  baseUri: string;
129
189
  errorUri: string;
190
+ successUri: string;
130
191
  provider: {
131
192
  [name: string]: Provider | undefined;
132
193
  };
@@ -135,4 +196,4 @@ interface OAuth2ClientConfig {
135
196
  };
136
197
  }
137
198
 
138
- export type { CodeExchangeParams, OAuth2AuthorizationRequest, OAuth2ClientConfig, OAuth2Token, Provider, RefreshTokenParams, Registration, RevokeTokenParams, StandardClaims, UserInfo };
199
+ export { type ExchangeCodeParams, type LoginOAuth2NativeParams, type NativeCredentials, type OAuth2AuthorizationRequest, type OAuth2ClientConfig, type OAuth2Token, OidcScopes, type OidcToken, type PkceParameters, type Provider, type RefreshTokenParams, type Registration, type RevokeTokenParams, type StandardClaims, type UserInfo };
@@ -1 +1,14 @@
1
+ // src/oauth2/types.ts
2
+ var OidcScopes = /* @__PURE__ */ ((OidcScopes2) => {
3
+ OidcScopes2["openid"] = "openid";
4
+ OidcScopes2["profile"] = "profile";
5
+ OidcScopes2["email"] = "email";
6
+ OidcScopes2["phone"] = "phone";
7
+ OidcScopes2["address"] = "address";
8
+ OidcScopes2["offline_access"] = "offline_access";
9
+ return OidcScopes2;
10
+ })(OidcScopes || {});
11
+ export {
12
+ OidcScopes
13
+ };
1
14
  //# sourceMappingURL=types.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../../src/oauth2/types.ts"],"sourcesContent":["// reference: https://datatracker.ietf.org/doc/html/rfc7636\nexport interface PkceParameters {\n code_verifier: string;\n code_challenge: string;\n code_challenge_method: 'S256' | 'plain';\n}\n\nexport interface OAuth2AuthorizationRequest {\n state: string;\n registrationId: string;\n authorizationRequestUri: string;\n additionalParameters: PkceParameters;\n}\n\ninterface CreateAuthorizationUriParams {\n state: string;\n clientId: string;\n redirectUri: string;\n scope: string[] | undefined;\n pkce?: Omit<PkceParameters, 'code_verifier'>;\n}\n\nexport interface ExchangeCodeParams {\n code: string;\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n pkce?: Omit<PkceParameters, 'code_challenge' | 'code_challenge_method'>;\n}\n\nexport interface RefreshTokenParams {\n clientId: string;\n clientSecret: string;\n refreshToken: string;\n}\n\nexport interface RevokeTokenParams {\n token: string;\n clientId: string;\n clientSecret: string;\n}\n\nexport interface LoginOAuth2NativeParams {\n clientId: string;\n clientSecret: string;\n redirectUri: string;\n pkce?: PkceParameters;\n credentials: NativeCredentials;\n}\n\n// reference: https://datatracker.ietf.org/doc/html/rfc6749#section-5.1\nexport interface OAuth2Token {\n /**\n * REQUIRED.\n * The access token issued by the authorization server.\n */\n access_token: string;\n\n /**\n * REQUIRED.\n * The type of the token issued as described in Section 7.1. Value is case insensitive.\n */\n token_type: 'Bearer' | 'bearer' | 'mac' | (string & {});\n\n /**\n * RECOMMENDED.\n * The lifetime in seconds of the access token. For example, the value \"3600\" denotes that the\n * access token will expire in one hour from the time the response was generated. If omitted, the\n * authorization server SHOULD provide the expiration time via other means or document the default\n * value.\n */\n expires_in?: number;\n\n /**\n * OPTIONAL.\n * The refresh token, which can be used to obtain new access tokens using the same authorization\n * grant as described in Section 6.\n */\n refresh_token?: string;\n\n /**\n * OPTIONAL.\n * If identical to the scope requested by the client; otherwise, REQUIRED. The scope of the\n * access token as described by Section 3.3.\n */\n scope?: string;\n\n id_token?: string;\n}\n\nexport interface OidcToken extends OAuth2Token {\n id_token: string;\n}\n\nexport type NativeCredentials = {\n state: string;\n code?: string;\n client_id?: string;\n redirect_uri?: string;\n id_token?: string;\n access_token?: string;\n};\n\nexport enum OidcScopes {\n openid = 'openid',\n profile = 'profile',\n email = 'email',\n phone = 'phone',\n address = 'address',\n offline_access = 'offline_access',\n}\n\n// https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims\nexport interface StandardClaims {\n sub: string;\n name?: string;\n given_name?: string;\n family_name?: string;\n middle_name?: string;\n nickname?: string;\n preferred_username?: string;\n profile?: string;\n picture?: string;\n website?: string;\n email?: string;\n email_verified?: boolean;\n gender?: 'female' | 'male' | string;\n birthdate?: string; // ISO 8601-1 [ISO8601‑1] YYYY-MM-DD format.\n zoneinfo?: string; // IANA Time Zone Database [IANA.time‑zones]\n locale?: string; // BCP47 [RFC5646] language tag.\n phone_number?: string; // E.164\n phone_number_verified?: boolean;\n address?: {\n formatted: string;\n street_address: string;\n locality: string;\n region: string;\n postal_code: string;\n country: string;\n }; // [RFC8259] JSON object\n updated_at?: number;\n}\n\nexport interface UserInfo<T = unknown> {\n claims: StandardClaims;\n data: T;\n}\n\nexport interface Provider {\n /** Authorization URI for the provider. */\n authorizationUri: string;\n\n /** Token URI for the provider. */\n tokenUri: string;\n\n tokenRevokeUri?: string;\n\n /** User info URI for the provider. */\n userInfoUri?: string;\n\n /** User info authentication method for the provider. */\n userInfoAuthenticationMethod?: string;\n\n /**\n * Name of the attribute that will be used to extract the username from the call to 'userInfoUri'.\n * */\n userNameAttribute?: string;\n\n /** JWK set URI for the provider */\n jwkSetUri?: string;\n\n /**\n * URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0 Authorization\n * Server Metadata endpoint defined by RFC 8414.\n * */\n issuerUri?: string;\n\n /** Default scopes for the provider. */\n defaultScope: string[];\n\n /** step 1: generate authorization uri and redirect user to it */\n createAuthorizationUri: (params: CreateAuthorizationUriParams) => URL;\n\n /** step 2: exchange code for token */\n exchangeAuthorizationCode: (params: ExchangeCodeParams) => Promise<OAuth2Token>;\n\n /** step 3: get user info */\n getUserInfo: (token: OAuth2Token) => Promise<UserInfo>;\n\n /** others: refresh access token */\n refreshAccessToken?: (params: RefreshTokenParams) => Promise<OAuth2Token>;\n\n /** others: revoke token */\n revokeToken?: (params: RevokeTokenParams) => Promise<void>;\n\n loginOAuth2Native?: (\n params: LoginOAuth2NativeParams\n ) => Promise<{ token: OAuth2Token; userInfo: UserInfo }>;\n}\n\nexport interface Registration {\n /**\n * Reference to the OAuth 2.0 provider to use. May reference an element from the 'provider'\n * property or used one of the commonly used providers (google, github, facebook, okta).\n * */\n provider?: string;\n\n /** Client ID for the registration. */\n clientId: string;\n\n /** Client secret of the registration. */\n clientSecret: string;\n\n /** Client authentication method. May be left blank when using a pre-defined provider. */\n clientAuthenticationMethod?: string;\n\n /** Authorization grant type. May be left blank when using a pre-defined provider. */\n authorizationGrantType?:\n | 'authorization_code' // default\n | 'client_credentials'\n | 'device_code'\n | 'jwt_bearer'\n // Deprecated. The latest OAuth 2.0 Security Best Current Practice disallows the use of the\n // Resource Owner Password Credentials grant.\n | 'password'\n | 'refresh_token'\n | 'token_exchange';\n\n /** Redirect URI. May be left blank when using a pre-defined provider. */\n redirectUri?: string;\n\n /** Authorization scopes. When left blank the provider's default scopes, if any, will be used. */\n scope?: string[];\n\n /** Client name. May be left blank when using a pre-defined provider. */\n clientName?: string;\n}\n\nexport interface OAuth2ClientConfig {\n baseUri: string;\n errorUri: string;\n successUri: string;\n provider: { [name: string]: Provider | undefined };\n registration: { [name: string]: Registration | undefined };\n}\n"],"mappings":";AAuGO,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,YAAS;AACT,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,WAAQ;AACR,EAAAA,YAAA,WAAQ;AACR,EAAAA,YAAA,aAAU;AACV,EAAAA,YAAA,oBAAiB;AANP,SAAAA;AAAA,GAAA;","names":["OidcScopes"]}
@@ -394,6 +394,27 @@ var RedisIndexedSessionRepository = class {
394
394
  async cleanupExpiredSessions() {
395
395
  await this.expirationStore.cleanupExpiredSessions();
396
396
  }
397
+ getItemKey(key) {
398
+ return `${this.namespace.replace("session", "storage")}:${key}`;
399
+ }
400
+ async setItem(key, value, expiresIn) {
401
+ const k = this.getItemKey(key);
402
+ const v = JSON.stringify(value);
403
+ if (expiresIn) {
404
+ await this.redis.set(k, v, "EX", expiresIn);
405
+ } else {
406
+ await this.redis.set(k, v);
407
+ }
408
+ }
409
+ async getItem(key) {
410
+ const k = this.getItemKey(key);
411
+ const json = await this.redis.get(k);
412
+ return json ? JSON.parse(json) : null;
413
+ }
414
+ async removeItem(key) {
415
+ const k = this.getItemKey(key);
416
+ await this.redis.del(k);
417
+ }
397
418
  };
398
419
  // Annotate the CommonJS export names for ESM import in node:
399
420
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/session/redis-session.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport type { Redis } from 'ioredis';\nimport type { Namespace, SecurityContext, Session, SessionRepository } from './types';\n\nconst DEFAULT_MAX_INACTIVE_INTERVAL = 1800;\n\nconst CREATION_TIME_KEY = 'creationTime';\nconst LAST_ACCESSED_TIME_KEY = 'lastAccessedTime';\nconst MAX_INACTIVE_INTERVAL_KEY = 'maxInactiveInterval';\nconst ATTRIBUTE_PREFIX = 'sessionAttr:';\nconst SESSION_EXPIRES_PREFIX = 'expires:';\n\nconst SPRING_SECURITY_CONTEXT = 'SPRING_SECURITY_CONTEXT';\nexport const PRINCIPAL_NAME_INDEX_NAME = `org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME`;\n\nfunction generateId() {\n return randomUUID();\n}\n\nclass MapSession implements Session {\n private id: string;\n private sessionAttrs = new Map<string, string | number>();\n private creationTime = Date.now();\n private lastAccessedTime = this.creationTime;\n private maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL;\n\n constructor(id?: string);\n constructor(session: Session);\n constructor(input?: string | Session) {\n if (typeof input === 'undefined') {\n this.id = generateId();\n } else if (typeof input === 'string') {\n this.id = input;\n } else {\n const session = input;\n this.id = session.getId();\n\n for (const attrName of session.getAttributeNames()) {\n const attrValue = session.getAttribute(attrName);\n if (attrValue !== null) {\n this.setAttribute(attrName, attrValue);\n }\n }\n\n this.lastAccessedTime = session.getLastAccessedTime();\n this.creationTime = session.getCreationTime();\n this.maxInactiveInterval = session.getMaxInactiveInterval();\n }\n }\n\n setCreationTime(creationTime: number) {\n this.creationTime = creationTime;\n }\n\n // override\n getId() {\n return this.id;\n }\n\n changeSessionId() {\n const changedId = generateId();\n this.id = changedId;\n return changedId;\n }\n\n getAttribute(name: string) {\n return this.sessionAttrs.get(name) ?? null;\n }\n\n getAttributeNames(): string[] {\n return Array.from(this.sessionAttrs.keys());\n }\n\n setAttribute(name: string, value: string | number) {\n if (value === null) {\n this.removeAttribute(name);\n } else {\n this.sessionAttrs.set(name, value);\n }\n }\n\n removeAttribute(name: string) {\n this.sessionAttrs.delete(name);\n }\n\n getCreationTime() {\n return this.creationTime;\n }\n\n getLastAccessedTime() {\n return this.lastAccessedTime;\n }\n\n setLastAccessedTime(lastAccessedTime: number) {\n this.lastAccessedTime = lastAccessedTime;\n }\n\n getMaxInactiveInterval() {\n return this.maxInactiveInterval;\n }\n\n setMaxInactiveInterval(interval: number) {\n this.maxInactiveInterval = interval;\n }\n\n isExpired() {\n if (this.maxInactiveInterval < 0) return false;\n return Date.now() - this.maxInactiveInterval * 1000 - this.lastAccessedTime >= 0;\n }\n}\n\nfunction resolveIndexesFor(session: Session): string | null {\n const principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME) as string | null;\n if (principalName !== null) return principalName;\n\n const jsonString = session.getAttribute(SPRING_SECURITY_CONTEXT);\n if (typeof jsonString === 'string') {\n const context = JSON.parse(jsonString) as SecurityContext;\n return context?.authentication?.name ?? context.authentication?.principal?.name ?? null;\n }\n return null;\n}\n\nclass RedisSession implements Session {\n private readonly cached: MapSession;\n\n public isNew: boolean;\n public delta = new Map<string, string | number | null>();\n public originalSessionId: string;\n public originalPrincipalName: string | null;\n public originalLastAccessTime: number | null = null;\n\n constructor(cached: MapSession, isNew: boolean) {\n this.cached = cached;\n this.isNew = isNew;\n this.originalSessionId = cached.getId();\n this.originalPrincipalName = resolveIndexesFor(this);\n\n if (isNew) {\n this.delta.set(CREATION_TIME_KEY, cached.getCreationTime());\n this.delta.set(LAST_ACCESSED_TIME_KEY, cached.getLastAccessedTime());\n this.delta.set(MAX_INACTIVE_INTERVAL_KEY, cached.getMaxInactiveInterval());\n this.cached.getAttributeNames().forEach((name) => {\n const value = this.cached.getAttribute(name);\n if (value) this.delta.set(this.getSessionAttrNameKey(name), value);\n });\n }\n }\n\n getSessionAttrNameKey(name: string) {\n return ATTRIBUTE_PREFIX + name;\n }\n\n // override\n setLastAccessedTime(lastAccessedTime: number) {\n this.cached.setLastAccessedTime(lastAccessedTime);\n this.delta.set(LAST_ACCESSED_TIME_KEY, lastAccessedTime);\n }\n\n isExpired() {\n return this.cached.isExpired();\n }\n\n getCreationTime() {\n return this.cached.getCreationTime();\n }\n\n getId() {\n return this.cached.getId();\n }\n\n changeSessionId() {\n return this.cached.changeSessionId();\n }\n\n getLastAccessedTime() {\n return this.cached.getLastAccessedTime();\n }\n\n setMaxInactiveInterval(interval: number) {\n this.cached.setMaxInactiveInterval(interval);\n this.delta.set(MAX_INACTIVE_INTERVAL_KEY, interval);\n }\n\n getMaxInactiveInterval() {\n return this.cached.getMaxInactiveInterval();\n }\n\n getAttribute(name: string) {\n return this.cached.getAttribute(name);\n }\n\n getAttributeNames() {\n return this.cached.getAttributeNames();\n }\n\n setAttribute(name: string, value: string) {\n this.cached.setAttribute(name, value);\n this.delta.set(this.getSessionAttrNameKey(name), value);\n }\n\n removeAttribute(name: string) {\n this.cached.removeAttribute(name);\n this.delta.set(this.getSessionAttrNameKey(name), null);\n }\n}\n\ninterface RedisSessionExpirationStore {\n save(session: RedisSession): Promise<void>;\n remove(sessionId: string): Promise<void>;\n rename(session: RedisSession): Promise<void>;\n cleanupExpiredSessions(): Promise<void>;\n}\n\nclass SortedSetRedisSessionExpirationStore implements RedisSessionExpirationStore {\n private readonly redis: Redis;\n private readonly namespace: Namespace;\n private readonly expirationsKey: string;\n\n private cleanupCount = 100;\n\n constructor(redis: Redis, namespace: Namespace) {\n this.redis = redis;\n this.namespace = namespace;\n this.expirationsKey = `${this.namespace}:sessions:expirations`;\n }\n\n getSessionKey(sessionId: string) {\n return `${this.namespace}:sessions:${sessionId}`;\n }\n\n async save(session: RedisSession) {\n const expirationInMillis =\n session.getLastAccessedTime() + session.getMaxInactiveInterval() * 1000;\n await this.redis.zadd(this.expirationsKey, expirationInMillis, session.getId());\n }\n\n async remove(sessionId: string) {\n await this.redis.zrem(this.expirationsKey, sessionId);\n }\n\n async rename(session: RedisSession) {\n if (session.originalSessionId === null || session.originalSessionId === session.getId()) return;\n await this.remove(session.originalSessionId);\n await this.save(session);\n }\n\n async cleanupExpiredSessions() {\n const sessionIds = await this.redis.zrevrangebyscore(\n this.expirationsKey,\n Date.now(),\n 0,\n 'LIMIT',\n 0,\n this.cleanupCount\n );\n if (sessionIds.length === 0) return;\n for (const sessionId of sessionIds) {\n const sessionKey = this.getSessionKey(sessionId);\n // Checks if the session exists. By trying to access the session we only trigger a deletion\n // if the TTL is expired.\n await this.redis.exists(sessionKey);\n }\n }\n}\n\n// 15549130 <spring:session:sessions:uuid, hash<session>> + 300s\n// 15549147 <spring:session:sessions:expirations, sorted_set<uuid>> + 300s\n// 15548839 <spring:session:sessions:expires:uuid, string<empty>>\nexport class RedisIndexedSessionRepository implements SessionRepository<RedisSession> {\n private readonly redis: Redis;\n private readonly namespace: Namespace;\n private readonly expirationStore: RedisSessionExpirationStore;\n\n private defaultMaxInactiveInterval = 1800;\n\n constructor(redis: Redis, namespace: Namespace = 'spring:session') {\n this.redis = redis;\n this.namespace = namespace;\n this.expirationStore = new SortedSetRedisSessionExpirationStore(redis, namespace);\n }\n\n setDefaultMaxInactiveInterval(interval: number) {\n this.defaultMaxInactiveInterval = interval;\n }\n\n private getSessionKey(sessionId: string) {\n return `${this.namespace}:sessions:${sessionId}`;\n }\n\n private getExpiredKey(sessionId: string) {\n return `${this.namespace}:sessions:expires:${sessionId}`;\n }\n\n private getSessionAttrNameKey(name: string) {\n return ATTRIBUTE_PREFIX + name;\n }\n\n private getPrincipalKey(principalName: string) {\n return `${this.namespace}:index:${PRINCIPAL_NAME_INDEX_NAME}:${principalName}`;\n }\n\n private async getSession(id: string, allowExpired: boolean): Promise<RedisSession | null> {\n const key = this.getSessionKey(id);\n const entries = await this.redis.hgetall(key);\n if (!entries || Object.keys(entries).length === 0) return null;\n const loaded = new MapSession(id);\n if (!entries.creationTime) throw new Error('creationTime attribute not found');\n if (!entries.lastAccessedTime) throw new Error('lastAccessedTime attribute not found');\n if (!entries.maxInactiveInterval) throw new Error('maxInactiveInterval attribute not found');\n\n loaded.setCreationTime(Number(entries.creationTime));\n loaded.setLastAccessedTime(Number(entries.lastAccessedTime));\n loaded.setMaxInactiveInterval(Number(entries.maxInactiveInterval));\n\n for (const [key, value] of Object.entries(entries)) {\n if (key.startsWith(ATTRIBUTE_PREFIX)) {\n loaded.setAttribute(key.slice(ATTRIBUTE_PREFIX.length), value);\n }\n }\n\n if (!allowExpired && loaded.isExpired()) return null;\n const result = new RedisSession(loaded, false);\n result.originalLastAccessTime = loaded.getLastAccessedTime();\n return result;\n }\n\n private async saveDelta(session: RedisSession) {\n if (session.delta.size === 0) return;\n const sessionId = session.getId();\n const delta = new Map<string, string | number>();\n const removes: string[] = [];\n session.delta.forEach((value, key) => {\n if (value !== null) delta.set(key, value);\n else removes.push(key);\n });\n\n if (delta.size !== 0) {\n await this.redis.hset(this.getSessionKey(sessionId), delta);\n }\n if (removes.length !== 0) {\n await this.redis.hdel(this.getSessionKey(sessionId), ...removes);\n }\n\n const principalSessionKey = this.getSessionAttrNameKey(PRINCIPAL_NAME_INDEX_NAME);\n const securityPrincipalSessionKey = this.getSessionAttrNameKey(SPRING_SECURITY_CONTEXT);\n if (session.delta.has(principalSessionKey) || session.delta.has(securityPrincipalSessionKey)) {\n if (session.originalPrincipalName !== null) {\n const originalPrincipalRedisKey = this.getPrincipalKey(session.originalPrincipalName);\n await this.redis.srem(originalPrincipalRedisKey, sessionId);\n }\n const principal = resolveIndexesFor(session);\n session.originalPrincipalName = principal;\n if (principal !== null) {\n const principalRedisKey = this.getPrincipalKey(principal);\n await this.redis.sadd(principalRedisKey, sessionId);\n }\n }\n\n if (session.isNew) {\n // here your can send session created message to channel\n session.isNew = false;\n }\n\n const sessionExpireInSeconds = session.getMaxInactiveInterval();\n\n // createShadowKey start\n const keyToExpire = SESSION_EXPIRES_PREFIX + session.getId();\n const sessionKey = this.getSessionKey(keyToExpire);\n\n if (sessionExpireInSeconds < 0) {\n await this.redis.append(sessionKey, '');\n await this.redis.persist(sessionKey);\n await this.redis.persist(this.getSessionKey(session.getId()));\n } else if (sessionExpireInSeconds === 0) {\n await this.redis.del(sessionKey);\n } else {\n await this.redis.append(sessionKey, '');\n await this.redis.expire(sessionKey, sessionExpireInSeconds);\n }\n // createShadowKey end\n\n if (sessionExpireInSeconds > 0) {\n const fiveMinutesAfterExpires = sessionExpireInSeconds + 5 * 60;\n await this.redis.expire(this.getSessionKey(session.getId()), fiveMinutesAfterExpires);\n }\n\n await this.expirationStore.save(session);\n session.delta.clear();\n }\n\n private async saveChangeSessionId(session: RedisSession) {\n const sessionId = session.getId();\n if (sessionId === session.originalSessionId) return;\n if (!session.isNew) {\n const originalSessionIdKey = this.getSessionKey(session.originalSessionId);\n const sessionIdKey = this.getSessionKey(sessionId);\n const originalExpiredKey = this.getExpiredKey(session.originalSessionId);\n const expiredKey = this.getExpiredKey(sessionId);\n\n await this.redis.rename(originalSessionIdKey, sessionIdKey);\n await this.redis.rename(originalExpiredKey, expiredKey);\n\n if (session.originalPrincipalName !== null) {\n const originalPrincipalRedisKey = this.getPrincipalKey(session.originalPrincipalName);\n await this.redis.srem(originalPrincipalRedisKey, session.originalSessionId);\n await this.redis.sadd(originalPrincipalRedisKey, sessionId);\n }\n await this.expirationStore.rename(session);\n }\n session.originalSessionId = sessionId;\n }\n\n // override\n createSession() {\n const cached = new MapSession();\n cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);\n return new RedisSession(cached, true);\n }\n\n async save(session: RedisSession) {\n await this.saveChangeSessionId(session);\n await this.saveDelta(session);\n }\n\n async findById(sessionId: string): Promise<RedisSession | null> {\n return this.getSession(sessionId, false);\n }\n\n async deleteById(sessionId: string) {\n const session = await this.getSession(sessionId, true);\n if (!session) return;\n // 1. cleanup index\n const principal = resolveIndexesFor(session);\n if (principal !== null) {\n await this.redis.srem(this.getPrincipalKey(principal), sessionId);\n }\n // 2. delete in set\n await this.expirationStore.remove(sessionId);\n // 3. delete expired key\n const expiredKey = this.getExpiredKey(sessionId);\n await this.redis.del(expiredKey);\n\n session.setMaxInactiveInterval(0);\n await this.save(session);\n }\n\n async findByIndexNameAndIndexValue(indexName: string, indexValue: string) {\n if (indexName !== PRINCIPAL_NAME_INDEX_NAME) {\n return new Map<string, RedisSession>();\n }\n\n const principalKey = this.getPrincipalKey(indexValue);\n const sessionIds = await this.redis.smembers(principalKey);\n if (!sessionIds || sessionIds.length === 0) {\n return new Map<string, RedisSession>();\n }\n const sessions = new Map<string, RedisSession>();\n for (const sessionId of sessionIds) {\n const session = await this.findById(sessionId);\n if (session !== null) {\n sessions.set(sessionId, session);\n }\n }\n return sessions;\n }\n\n async findByPrincipalName(principalName: string) {\n return this.findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);\n }\n\n async cleanupExpiredSessions() {\n await this.expirationStore.cleanupExpiredSessions();\n }\n}\n"],"mappings":"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","names":["key"]}
1
+ {"version":3,"sources":["../../src/session/redis-session.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport type { Redis } from 'ioredis';\nimport type { Namespace, SecurityContext, Session, SessionRepository } from './types';\n\nconst DEFAULT_MAX_INACTIVE_INTERVAL = 1800;\n\nconst CREATION_TIME_KEY = 'creationTime';\nconst LAST_ACCESSED_TIME_KEY = 'lastAccessedTime';\nconst MAX_INACTIVE_INTERVAL_KEY = 'maxInactiveInterval';\nconst ATTRIBUTE_PREFIX = 'sessionAttr:';\nconst SESSION_EXPIRES_PREFIX = 'expires:';\n\nconst SPRING_SECURITY_CONTEXT = 'SPRING_SECURITY_CONTEXT';\nexport const PRINCIPAL_NAME_INDEX_NAME = `org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME`;\n\nfunction generateId() {\n return randomUUID();\n}\n\nclass MapSession implements Session {\n private id: string;\n private sessionAttrs = new Map<string, string | number>();\n private creationTime = Date.now();\n private lastAccessedTime = this.creationTime;\n private maxInactiveInterval = DEFAULT_MAX_INACTIVE_INTERVAL;\n\n constructor(id?: string);\n constructor(session: Session);\n constructor(input?: string | Session) {\n if (typeof input === 'undefined') {\n this.id = generateId();\n } else if (typeof input === 'string') {\n this.id = input;\n } else {\n const session = input;\n this.id = session.getId();\n\n for (const attrName of session.getAttributeNames()) {\n const attrValue = session.getAttribute(attrName);\n if (attrValue !== null) {\n this.setAttribute(attrName, attrValue);\n }\n }\n\n this.lastAccessedTime = session.getLastAccessedTime();\n this.creationTime = session.getCreationTime();\n this.maxInactiveInterval = session.getMaxInactiveInterval();\n }\n }\n\n setCreationTime(creationTime: number) {\n this.creationTime = creationTime;\n }\n\n // override\n getId() {\n return this.id;\n }\n\n changeSessionId() {\n const changedId = generateId();\n this.id = changedId;\n return changedId;\n }\n\n getAttribute(name: string) {\n return this.sessionAttrs.get(name) ?? null;\n }\n\n getAttributeNames(): string[] {\n return Array.from(this.sessionAttrs.keys());\n }\n\n setAttribute(name: string, value: string | number) {\n if (value === null) {\n this.removeAttribute(name);\n } else {\n this.sessionAttrs.set(name, value);\n }\n }\n\n removeAttribute(name: string) {\n this.sessionAttrs.delete(name);\n }\n\n getCreationTime() {\n return this.creationTime;\n }\n\n getLastAccessedTime() {\n return this.lastAccessedTime;\n }\n\n setLastAccessedTime(lastAccessedTime: number) {\n this.lastAccessedTime = lastAccessedTime;\n }\n\n getMaxInactiveInterval() {\n return this.maxInactiveInterval;\n }\n\n setMaxInactiveInterval(interval: number) {\n this.maxInactiveInterval = interval;\n }\n\n isExpired() {\n if (this.maxInactiveInterval < 0) return false;\n return Date.now() - this.maxInactiveInterval * 1000 - this.lastAccessedTime >= 0;\n }\n}\n\nfunction resolveIndexesFor(session: Session): string | null {\n const principalName = session.getAttribute(PRINCIPAL_NAME_INDEX_NAME) as string | null;\n if (principalName !== null) return principalName;\n\n const jsonString = session.getAttribute(SPRING_SECURITY_CONTEXT);\n if (typeof jsonString === 'string') {\n const context = JSON.parse(jsonString) as SecurityContext;\n return context?.authentication?.name ?? context.authentication?.principal?.name ?? null;\n }\n return null;\n}\n\nclass RedisSession implements Session {\n private readonly cached: MapSession;\n\n public isNew: boolean;\n public delta = new Map<string, string | number | null>();\n public originalSessionId: string;\n public originalPrincipalName: string | null;\n public originalLastAccessTime: number | null = null;\n\n constructor(cached: MapSession, isNew: boolean) {\n this.cached = cached;\n this.isNew = isNew;\n this.originalSessionId = cached.getId();\n this.originalPrincipalName = resolveIndexesFor(this);\n\n if (isNew) {\n this.delta.set(CREATION_TIME_KEY, cached.getCreationTime());\n this.delta.set(LAST_ACCESSED_TIME_KEY, cached.getLastAccessedTime());\n this.delta.set(MAX_INACTIVE_INTERVAL_KEY, cached.getMaxInactiveInterval());\n this.cached.getAttributeNames().forEach((name) => {\n const value = this.cached.getAttribute(name);\n if (value) this.delta.set(this.getSessionAttrNameKey(name), value);\n });\n }\n }\n\n getSessionAttrNameKey(name: string) {\n return ATTRIBUTE_PREFIX + name;\n }\n\n // override\n setLastAccessedTime(lastAccessedTime: number) {\n this.cached.setLastAccessedTime(lastAccessedTime);\n this.delta.set(LAST_ACCESSED_TIME_KEY, lastAccessedTime);\n }\n\n isExpired() {\n return this.cached.isExpired();\n }\n\n getCreationTime() {\n return this.cached.getCreationTime();\n }\n\n getId() {\n return this.cached.getId();\n }\n\n changeSessionId() {\n return this.cached.changeSessionId();\n }\n\n getLastAccessedTime() {\n return this.cached.getLastAccessedTime();\n }\n\n setMaxInactiveInterval(interval: number) {\n this.cached.setMaxInactiveInterval(interval);\n this.delta.set(MAX_INACTIVE_INTERVAL_KEY, interval);\n }\n\n getMaxInactiveInterval() {\n return this.cached.getMaxInactiveInterval();\n }\n\n getAttribute(name: string) {\n return this.cached.getAttribute(name);\n }\n\n getAttributeNames() {\n return this.cached.getAttributeNames();\n }\n\n setAttribute(name: string, value: string) {\n this.cached.setAttribute(name, value);\n this.delta.set(this.getSessionAttrNameKey(name), value);\n }\n\n removeAttribute(name: string) {\n this.cached.removeAttribute(name);\n this.delta.set(this.getSessionAttrNameKey(name), null);\n }\n}\n\ninterface RedisSessionExpirationStore {\n save(session: RedisSession): Promise<void>;\n remove(sessionId: string): Promise<void>;\n rename(session: RedisSession): Promise<void>;\n cleanupExpiredSessions(): Promise<void>;\n}\n\nclass SortedSetRedisSessionExpirationStore implements RedisSessionExpirationStore {\n private readonly redis: Redis;\n private readonly namespace: Namespace;\n private readonly expirationsKey: string;\n\n private cleanupCount = 100;\n\n constructor(redis: Redis, namespace: Namespace) {\n this.redis = redis;\n this.namespace = namespace;\n this.expirationsKey = `${this.namespace}:sessions:expirations`;\n }\n\n getSessionKey(sessionId: string) {\n return `${this.namespace}:sessions:${sessionId}`;\n }\n\n async save(session: RedisSession) {\n const expirationInMillis =\n session.getLastAccessedTime() + session.getMaxInactiveInterval() * 1000;\n await this.redis.zadd(this.expirationsKey, expirationInMillis, session.getId());\n }\n\n async remove(sessionId: string) {\n await this.redis.zrem(this.expirationsKey, sessionId);\n }\n\n async rename(session: RedisSession) {\n if (session.originalSessionId === null || session.originalSessionId === session.getId()) return;\n await this.remove(session.originalSessionId);\n await this.save(session);\n }\n\n async cleanupExpiredSessions() {\n const sessionIds = await this.redis.zrevrangebyscore(\n this.expirationsKey,\n Date.now(),\n 0,\n 'LIMIT',\n 0,\n this.cleanupCount\n );\n if (sessionIds.length === 0) return;\n for (const sessionId of sessionIds) {\n const sessionKey = this.getSessionKey(sessionId);\n // Checks if the session exists. By trying to access the session we only trigger a deletion\n // if the TTL is expired.\n await this.redis.exists(sessionKey);\n }\n }\n}\n\n// 15549130 <spring:session:sessions:uuid, hash<session>> + 300s\n// 15549147 <spring:session:sessions:expirations, sorted_set<uuid>> + 300s\n// 15548839 <spring:session:sessions:expires:uuid, string<empty>>\nexport class RedisIndexedSessionRepository implements SessionRepository<RedisSession> {\n private readonly redis: Redis;\n private readonly namespace: Namespace;\n private readonly expirationStore: RedisSessionExpirationStore;\n\n private defaultMaxInactiveInterval = 1800;\n\n constructor(redis: Redis, namespace: Namespace = 'spring:session') {\n this.redis = redis;\n this.namespace = namespace;\n this.expirationStore = new SortedSetRedisSessionExpirationStore(redis, namespace);\n }\n\n setDefaultMaxInactiveInterval(interval: number) {\n this.defaultMaxInactiveInterval = interval;\n }\n\n private getSessionKey(sessionId: string) {\n return `${this.namespace}:sessions:${sessionId}`;\n }\n\n private getExpiredKey(sessionId: string) {\n return `${this.namespace}:sessions:expires:${sessionId}`;\n }\n\n private getSessionAttrNameKey(name: string) {\n return ATTRIBUTE_PREFIX + name;\n }\n\n private getPrincipalKey(principalName: string) {\n return `${this.namespace}:index:${PRINCIPAL_NAME_INDEX_NAME}:${principalName}`;\n }\n\n private async getSession(id: string, allowExpired: boolean): Promise<RedisSession | null> {\n const key = this.getSessionKey(id);\n const entries = await this.redis.hgetall(key);\n if (!entries || Object.keys(entries).length === 0) return null;\n const loaded = new MapSession(id);\n if (!entries.creationTime) throw new Error('creationTime attribute not found');\n if (!entries.lastAccessedTime) throw new Error('lastAccessedTime attribute not found');\n if (!entries.maxInactiveInterval) throw new Error('maxInactiveInterval attribute not found');\n\n loaded.setCreationTime(Number(entries.creationTime));\n loaded.setLastAccessedTime(Number(entries.lastAccessedTime));\n loaded.setMaxInactiveInterval(Number(entries.maxInactiveInterval));\n\n for (const [key, value] of Object.entries(entries)) {\n if (key.startsWith(ATTRIBUTE_PREFIX)) {\n loaded.setAttribute(key.slice(ATTRIBUTE_PREFIX.length), value);\n }\n }\n\n if (!allowExpired && loaded.isExpired()) return null;\n const result = new RedisSession(loaded, false);\n result.originalLastAccessTime = loaded.getLastAccessedTime();\n return result;\n }\n\n private async saveDelta(session: RedisSession) {\n if (session.delta.size === 0) return;\n const sessionId = session.getId();\n const delta = new Map<string, string | number>();\n const removes: string[] = [];\n session.delta.forEach((value, key) => {\n if (value !== null) delta.set(key, value);\n else removes.push(key);\n });\n\n if (delta.size !== 0) {\n await this.redis.hset(this.getSessionKey(sessionId), delta);\n }\n if (removes.length !== 0) {\n await this.redis.hdel(this.getSessionKey(sessionId), ...removes);\n }\n\n const principalSessionKey = this.getSessionAttrNameKey(PRINCIPAL_NAME_INDEX_NAME);\n const securityPrincipalSessionKey = this.getSessionAttrNameKey(SPRING_SECURITY_CONTEXT);\n if (session.delta.has(principalSessionKey) || session.delta.has(securityPrincipalSessionKey)) {\n if (session.originalPrincipalName !== null) {\n const originalPrincipalRedisKey = this.getPrincipalKey(session.originalPrincipalName);\n await this.redis.srem(originalPrincipalRedisKey, sessionId);\n }\n const principal = resolveIndexesFor(session);\n session.originalPrincipalName = principal;\n if (principal !== null) {\n const principalRedisKey = this.getPrincipalKey(principal);\n await this.redis.sadd(principalRedisKey, sessionId);\n }\n }\n\n if (session.isNew) {\n // here your can send session created message to channel\n session.isNew = false;\n }\n\n const sessionExpireInSeconds = session.getMaxInactiveInterval();\n\n // createShadowKey start\n const keyToExpire = SESSION_EXPIRES_PREFIX + session.getId();\n const sessionKey = this.getSessionKey(keyToExpire);\n\n if (sessionExpireInSeconds < 0) {\n await this.redis.append(sessionKey, '');\n await this.redis.persist(sessionKey);\n await this.redis.persist(this.getSessionKey(session.getId()));\n } else if (sessionExpireInSeconds === 0) {\n await this.redis.del(sessionKey);\n } else {\n await this.redis.append(sessionKey, '');\n await this.redis.expire(sessionKey, sessionExpireInSeconds);\n }\n // createShadowKey end\n\n if (sessionExpireInSeconds > 0) {\n const fiveMinutesAfterExpires = sessionExpireInSeconds + 5 * 60;\n await this.redis.expire(this.getSessionKey(session.getId()), fiveMinutesAfterExpires);\n }\n\n await this.expirationStore.save(session);\n session.delta.clear();\n }\n\n private async saveChangeSessionId(session: RedisSession) {\n const sessionId = session.getId();\n if (sessionId === session.originalSessionId) return;\n if (!session.isNew) {\n const originalSessionIdKey = this.getSessionKey(session.originalSessionId);\n const sessionIdKey = this.getSessionKey(sessionId);\n const originalExpiredKey = this.getExpiredKey(session.originalSessionId);\n const expiredKey = this.getExpiredKey(sessionId);\n\n await this.redis.rename(originalSessionIdKey, sessionIdKey);\n await this.redis.rename(originalExpiredKey, expiredKey);\n\n if (session.originalPrincipalName !== null) {\n const originalPrincipalRedisKey = this.getPrincipalKey(session.originalPrincipalName);\n await this.redis.srem(originalPrincipalRedisKey, session.originalSessionId);\n await this.redis.sadd(originalPrincipalRedisKey, sessionId);\n }\n await this.expirationStore.rename(session);\n }\n session.originalSessionId = sessionId;\n }\n\n // override\n createSession() {\n const cached = new MapSession();\n cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);\n return new RedisSession(cached, true);\n }\n\n async save(session: RedisSession) {\n await this.saveChangeSessionId(session);\n await this.saveDelta(session);\n }\n\n async findById(sessionId: string): Promise<RedisSession | null> {\n return this.getSession(sessionId, false);\n }\n\n async deleteById(sessionId: string) {\n const session = await this.getSession(sessionId, true);\n if (!session) return;\n // 1. cleanup index\n const principal = resolveIndexesFor(session);\n if (principal !== null) {\n await this.redis.srem(this.getPrincipalKey(principal), sessionId);\n }\n // 2. delete in set\n await this.expirationStore.remove(sessionId);\n // 3. delete expired key\n const expiredKey = this.getExpiredKey(sessionId);\n await this.redis.del(expiredKey);\n\n session.setMaxInactiveInterval(0);\n await this.save(session);\n }\n\n async findByIndexNameAndIndexValue(indexName: string, indexValue: string) {\n if (indexName !== PRINCIPAL_NAME_INDEX_NAME) {\n return new Map<string, RedisSession>();\n }\n\n const principalKey = this.getPrincipalKey(indexValue);\n const sessionIds = await this.redis.smembers(principalKey);\n if (!sessionIds || sessionIds.length === 0) {\n return new Map<string, RedisSession>();\n }\n const sessions = new Map<string, RedisSession>();\n for (const sessionId of sessionIds) {\n const session = await this.findById(sessionId);\n if (session !== null) {\n sessions.set(sessionId, session);\n }\n }\n return sessions;\n }\n\n async findByPrincipalName(principalName: string) {\n return this.findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);\n }\n\n async cleanupExpiredSessions() {\n await this.expirationStore.cleanupExpiredSessions();\n }\n\n private getItemKey(key: string) {\n return `${this.namespace.replace('session', 'storage')}:${key}`;\n }\n\n async setItem(key: string, value: unknown, expiresIn?: number) {\n const k = this.getItemKey(key);\n const v = JSON.stringify(value);\n if (expiresIn) {\n await this.redis.set(k, v, 'EX', expiresIn);\n } else {\n await this.redis.set(k, v);\n }\n }\n\n async getItem<T = unknown>(key: string): Promise<T | null> {\n const k = this.getItemKey(key);\n const json = await this.redis.get(k);\n return json ? (JSON.parse(json) as T) : null;\n }\n\n async removeItem(key: string) {\n const k = this.getItemKey(key);\n await this.redis.del(k);\n }\n}\n"],"mappings":"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","names":["key"]}
@@ -68,6 +68,10 @@ declare class RedisIndexedSessionRepository implements SessionRepository<RedisSe
68
68
  findByIndexNameAndIndexValue(indexName: string, indexValue: string): Promise<Map<string, RedisSession>>;
69
69
  findByPrincipalName(principalName: string): Promise<Map<string, RedisSession>>;
70
70
  cleanupExpiredSessions(): Promise<void>;
71
+ private getItemKey;
72
+ setItem(key: string, value: unknown, expiresIn?: number): Promise<void>;
73
+ getItem<T = unknown>(key: string): Promise<T | null>;
74
+ removeItem(key: string): Promise<void>;
71
75
  }
72
76
 
73
77
  export { PRINCIPAL_NAME_INDEX_NAME, RedisIndexedSessionRepository };
@@ -68,6 +68,10 @@ declare class RedisIndexedSessionRepository implements SessionRepository<RedisSe
68
68
  findByIndexNameAndIndexValue(indexName: string, indexValue: string): Promise<Map<string, RedisSession>>;
69
69
  findByPrincipalName(principalName: string): Promise<Map<string, RedisSession>>;
70
70
  cleanupExpiredSessions(): Promise<void>;
71
+ private getItemKey;
72
+ setItem(key: string, value: unknown, expiresIn?: number): Promise<void>;
73
+ getItem<T = unknown>(key: string): Promise<T | null>;
74
+ removeItem(key: string): Promise<void>;
71
75
  }
72
76
 
73
77
  export { PRINCIPAL_NAME_INDEX_NAME, RedisIndexedSessionRepository };
@@ -369,6 +369,27 @@ var RedisIndexedSessionRepository = class {
369
369
  async cleanupExpiredSessions() {
370
370
  await this.expirationStore.cleanupExpiredSessions();
371
371
  }
372
+ getItemKey(key) {
373
+ return `${this.namespace.replace("session", "storage")}:${key}`;
374
+ }
375
+ async setItem(key, value, expiresIn) {
376
+ const k = this.getItemKey(key);
377
+ const v = JSON.stringify(value);
378
+ if (expiresIn) {
379
+ await this.redis.set(k, v, "EX", expiresIn);
380
+ } else {
381
+ await this.redis.set(k, v);
382
+ }
383
+ }
384
+ async getItem(key) {
385
+ const k = this.getItemKey(key);
386
+ const json = await this.redis.get(k);
387
+ return json ? JSON.parse(json) : null;
388
+ }
389
+ async removeItem(key) {
390
+ const k = this.getItemKey(key);
391
+ await this.redis.del(k);
392
+ }
372
393
  };
373
394
  export {
374
395
  PRINCIPAL_NAME_INDEX_NAME,