@shware/security 1.4.4 → 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/index.cjs +2 -2
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +1 -1
- package/dist/auth/index.d.ts +1 -1
- package/dist/auth/index.mjs +2 -2
- package/dist/auth/index.mjs.map +1 -1
- package/dist/oauth2/client.cjs +1 -1
- package/dist/oauth2/client.cjs.map +1 -1
- package/dist/oauth2/client.mjs +1 -1
- package/dist/oauth2/client.mjs.map +1 -1
- package/dist/oauth2/provider/apple.cjs.map +1 -1
- package/dist/oauth2/provider/apple.mjs.map +1 -1
- package/dist/oauth2/provider/facebook.cjs.map +1 -1
- package/dist/oauth2/provider/facebook.mjs.map +1 -1
- package/dist/oauth2/provider/github.cjs.map +1 -1
- package/dist/oauth2/provider/github.mjs.map +1 -1
- package/dist/oauth2/provider/google.cjs.map +1 -1
- package/dist/oauth2/provider/google.mjs.map +1 -1
- package/dist/oauth2/provider/wechat.cjs.map +1 -1
- package/dist/oauth2/provider/wechat.mjs.map +1 -1
- package/dist/oauth2/provider/x.cjs.map +1 -1
- package/dist/oauth2/provider/x.mjs.map +1 -1
- package/dist/session/db.cjs +1 -1
- package/dist/session/db.cjs.map +1 -1
- package/dist/session/db.mjs +1 -1
- package/dist/session/db.mjs.map +1 -1
- package/dist/session/redis.cjs.map +1 -1
- package/dist/session/redis.d.cts +1 -1
- package/dist/session/redis.d.ts +1 -1
- package/dist/session/redis.mjs.map +1 -1
- package/package.json +12 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth2/provider/github.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth2/provider/github.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { OAuth2Token, Provider } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode } from './common';\n\nexport function createGithubProvider(): Provider {\n return {\n authorizationUri: 'https://github.com/login/oauth/authorize',\n tokenUri: 'https://github.com/login/oauth/access_token',\n userInfoUri: 'https://api.github.com/user',\n userNameAttribute: 'id',\n defaultScope: ['user:email'],\n createAuthorizationUri({ pkce, ...params }) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode({ pkce, ...params }) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GithubErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GithubToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n throw new OAuth2Error(response.status, 'invalid_request', 'Failed to fetch user info');\n }\n const data = (await response.json()) as GithubUserInfo;\n const emailResponse = await fetch('https://api.github.com/user/emails', { headers });\n if (!emailResponse.ok) {\n throw new OAuth2Error(\n emailResponse.status,\n 'invalid_request',\n 'Failed to fetch user email'\n );\n }\n const emails = (await emailResponse.json()) as GithubEmail[];\n data.email = emails.find((email) => email.primary)?.email || data.email;\n const email_verified = emails.find((email) => email.email === data.email)?.verified || false;\n return {\n data,\n claims: {\n sub: data.id,\n name: data.name || data.login,\n email_verified,\n email: data.email,\n picture: data.avatar_url,\n profile: data.html_url,\n website: data.html_url,\n updated_at: data.updated_at ? Date.parse(data.updated_at) : undefined,\n },\n };\n },\n };\n}\n\nexport const github = createGithubProvider();\n\nexport interface GithubUserInfo {\n login: string;\n id: string;\n node_id: string;\n avatar_url: string;\n gravatar_id: string;\n url: string;\n html_url: string;\n followers_url: string;\n following_url: string;\n gists_url: string;\n starred_url: string;\n subscriptions_url: string;\n organizations_url: string;\n repos_url: string;\n events_url: string;\n received_events_url: string;\n type: string;\n site_admin: boolean;\n name: string;\n company: string;\n blog: string;\n location: string;\n email: string;\n hireable: boolean;\n bio: string;\n twitter_username: string;\n public_repos: string;\n public_gists: string;\n followers: string;\n following: string;\n created_at: string;\n updated_at: string;\n private_gists: string;\n total_private_repos: string;\n owned_private_repos: string;\n disk_usage: string;\n collaborators: string;\n two_factor_authentication: boolean;\n plan: {\n name: string;\n space: string;\n private_repos: string;\n collaborators: string;\n };\n}\n\ninterface GithubEmail {\n email: string;\n primary: boolean;\n verified: boolean;\n visibility: 'public' | 'private';\n}\n\nexport interface GithubToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n scope: string;\n expires_in: number;\n refresh_token: string;\n}\n\ninterface GithubErrorResponse {\n error: string;\n error_description: string;\n error_uri: string;\n}\n"],"mappings":";AAAA,OAAO,eAAe;AACtB,SAAS,mBAAmB;AAE5B,SAAS,wBAAwB,iCAAiC;AAE3D,SAAS,uBAAiC;AAC/C,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,cAAc,CAAC,YAAY;AAAA,IAC3B,uBAAuB,EAAE,MAAM,GAAG,OAAO,GAAG;AAC1C,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,EAAE,MAAM,GAAG,OAAO,GAAG;AACnD,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,YAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,aAAa,GAAG;AAClC,gBAAU,cAAc,0BAA0B;AAClD,gBAAU,KAAK,aAAa,yBAAyB;AACrD,YAAM,UAAU,EAAE,QAAQ,oBAAoB,eAAe,UAAU,YAAY,GAAG;AACtF,YAAM,WAAW,MAAM,MAAM,KAAK,aAAa,EAAE,QAAQ,CAAC;AAC1D,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,YAAY,SAAS,QAAQ,mBAAmB,2BAA2B;AAAA,MACvF;AACA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,YAAM,gBAAgB,MAAM,MAAM,sCAAsC,EAAE,QAAQ,CAAC;AACnF,UAAI,CAAC,cAAc,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,cAAc;AAAA,UACd;AAAA,UACA;AAAA,QACF;AAAA,MACF;AACA,YAAM,SAAU,MAAM,cAAc,KAAK;AACzC,WAAK,QAAQ,OAAO,KAAK,CAAC,UAAU,MAAM,OAAO,GAAG,SAAS,KAAK;AAClE,YAAM,iBAAiB,OAAO,KAAK,CAAC,UAAU,MAAM,UAAU,KAAK,KAAK,GAAG,YAAY;AACvF,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK,QAAQ,KAAK;AAAA,UACxB;AAAA,UACA,OAAO,KAAK;AAAA,UACZ,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,SAAS,KAAK;AAAA,UACd,YAAY,KAAK,aAAa,KAAK,MAAM,KAAK,UAAU,IAAI;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth2/provider/google.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth2/provider/google.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { LoginOAuth2NativeParams, NativeCredential, OAuth2Token, OneTapProvider } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\nexport function createGoogleProvider(): OneTapProvider {\n return {\n authorizationUri: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUri: 'https://oauth2.googleapis.com/token',\n userInfoUri: 'https://www.googleapis.com/oauth2/v3/userinfo',\n userNameAttribute: 'sub',\n jwkSetUri: 'https://www.googleapis.com/oauth2/v3/certs',\n // https://developers.google.com/identity/protocols/oauth2/scopes#oauth2\n defaultScope: [\n 'https://www.googleapis.com/auth/userinfo.email',\n 'https://www.googleapis.com/auth/userinfo.profile',\n 'openid',\n ],\n createAuthorizationUri(params) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GoogleToken;\n },\n async getUserInfo({ id_token }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n };\n },\n async loginOAuth2Native({ pkce, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { code } = credentials;\n const { tokenUri } = this;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n const token = (await response.json()) as GoogleToken;\n const userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n async getTokenInfo(id_token: string) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n token: {\n id_token,\n access_token: '',\n token_type: '',\n expires_in: 0,\n refresh_token: '',\n scope: '',\n },\n userInfo: {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n },\n };\n },\n };\n}\n\nexport const google = createGoogleProvider();\n\nexport interface GoogleUserInfo {\n aud: string;\n azp: string;\n email: string;\n email_verified: boolean;\n exp: number;\n family_name: string;\n given_name: string;\n hd?: string;\n iat: number;\n iss: string;\n jti?: string;\n locale?: string;\n name: string;\n nbf?: number;\n picture: string;\n sub: string;\n nonce?: string;\n}\n\nexport interface GoogleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n scope: string;\n}\n\n// android,ios has different client_id and redirect_uri\nexport interface GoogleAppCredential extends NativeCredential {\n state: string;\n code: string;\n client_id: string;\n redirect_uri: string;\n}\n\nexport interface GoogleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_token'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'access_denied'\n | 'server_error';\n error_description: string;\n}\n"],"mappings":"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","names":["invariant"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth2/provider/google.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth2/provider/google.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { LoginOAuth2NativeParams, NativeCredential, OAuth2Token, OneTapProvider } from '../types';\nimport { createAuthorizationUri, exchangeAuthorizationCode, verifyIdToken } from './common';\n\nexport function createGoogleProvider(): OneTapProvider {\n return {\n authorizationUri: 'https://accounts.google.com/o/oauth2/v2/auth',\n tokenUri: 'https://oauth2.googleapis.com/token',\n userInfoUri: 'https://www.googleapis.com/oauth2/v3/userinfo',\n userNameAttribute: 'sub',\n jwkSetUri: 'https://www.googleapis.com/oauth2/v3/certs',\n // https://developers.google.com/identity/protocols/oauth2/scopes#oauth2\n defaultScope: [\n 'https://www.googleapis.com/auth/userinfo.email',\n 'https://www.googleapis.com/auth/userinfo.profile',\n 'openid',\n ],\n createAuthorizationUri(params) {\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n const response = await exchangeAuthorizationCode({ ...params, tokenUri: this.tokenUri });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as GoogleToken;\n },\n async getUserInfo({ id_token }) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n };\n },\n async loginOAuth2Native({ pkce, credentials, ...params }: LoginOAuth2NativeParams) {\n invariant(credentials.code, 'code is required');\n const { code } = credentials;\n const { tokenUri } = this;\n const response = await exchangeAuthorizationCode({ code, tokenUri, ...params });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as GoogleErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n const token = (await response.json()) as GoogleToken;\n const userInfo = await this.getUserInfo(token);\n return { token, userInfo };\n },\n async getTokenInfo(id_token: string) {\n invariant(id_token, 'id_token is required');\n invariant(this.jwkSetUri, 'jwkSetUri is required');\n const data = await verifyIdToken<GoogleUserInfo>(id_token, this.jwkSetUri);\n return {\n token: {\n id_token,\n access_token: '',\n token_type: '',\n expires_in: 0,\n refresh_token: '',\n scope: '',\n },\n userInfo: {\n data,\n claims: {\n sub: data.sub,\n name: data.name,\n picture: data.picture,\n email: data.email,\n email_verified: data.email_verified,\n given_name: data.given_name,\n family_name: data.family_name,\n locale: data.locale,\n },\n },\n };\n },\n };\n}\n\nexport const google = createGoogleProvider();\n\nexport interface GoogleUserInfo {\n aud: string;\n azp: string;\n email: string;\n email_verified: boolean;\n exp: number;\n family_name: string;\n given_name: string;\n hd?: string;\n iat: number;\n iss: string;\n jti?: string;\n locale?: string;\n name: string;\n nbf?: number;\n picture: string;\n sub: string;\n nonce?: string;\n}\n\nexport interface GoogleToken extends OAuth2Token {\n access_token: string;\n token_type: string;\n expires_in: number;\n refresh_token: string;\n id_token: string;\n scope: string;\n}\n\n// android,ios has different client_id and redirect_uri\nexport interface GoogleAppCredential extends NativeCredential {\n state: string;\n code: string;\n client_id: string;\n redirect_uri: string;\n}\n\nexport interface GoogleErrorResponse {\n error:\n | 'invalid_request'\n | 'invalid_client'\n | 'invalid_grant'\n | 'invalid_token'\n | 'invalid_scope'\n | 'unauthorized_client'\n | 'unsupported_grant_type'\n | 'access_denied'\n | 'server_error';\n error_description: string;\n}\n"],"mappings":";AAAA,OAAO,eAAe;AACtB,SAAS,mBAAmB;AAE5B,SAAS,wBAAwB,2BAA2B,qBAAqB;AAE1E,SAAS,uBAAuC;AACrD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,mBAAmB;AAAA,IACnB,WAAW;AAAA;AAAA,IAEX,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,uBAAuB,QAAQ;AAC7B,aAAO,uBAAuB;AAAA,QAC5B,GAAG;AAAA,QACH,OAAO,OAAO,SAAS,KAAK;AAAA,QAC5B,kBAAkB,KAAK;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,IACA,MAAM,0BAA0B,QAAQ;AACtC,YAAM,WAAW,MAAM,0BAA0B,EAAE,GAAG,QAAQ,UAAU,KAAK,SAAS,CAAC;AACvF,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,YAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B;AAAA,IACA,MAAM,YAAY,EAAE,SAAS,GAAG;AAC9B,gBAAU,UAAU,sBAAsB;AAC1C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,MAAM,cAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,UACN,KAAK,KAAK;AAAA,UACV,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,gBAAgB,KAAK;AAAA,UACrB,YAAY,KAAK;AAAA,UACjB,aAAa,KAAK;AAAA,UAClB,QAAQ,KAAK;AAAA,QACf;AAAA,MACF;AAAA,IACF;AAAA,IACA,MAAM,kBAAkB,EAAE,MAAM,aAAa,GAAG,OAAO,GAA4B;AACjF,gBAAU,YAAY,MAAM,kBAAkB;AAC9C,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,EAAE,SAAS,IAAI;AACrB,YAAM,WAAW,MAAM,0BAA0B,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC;AAC9E,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,EAAE,OAAO,kBAAkB,IAAK,MAAM,SAAS,KAAK;AAC1D,cAAM,IAAI,YAAY,SAAS,QAAQ,OAAO,iBAAiB;AAAA,MACjE;AACA,YAAM,QAAS,MAAM,SAAS,KAAK;AACnC,YAAM,WAAW,MAAM,KAAK,YAAY,KAAK;AAC7C,aAAO,EAAE,OAAO,SAAS;AAAA,IAC3B;AAAA,IACA,MAAM,aAAa,UAAkB;AACnC,gBAAU,UAAU,sBAAsB;AAC1C,gBAAU,KAAK,WAAW,uBAAuB;AACjD,YAAM,OAAO,MAAM,cAA8B,UAAU,KAAK,SAAS;AACzE,aAAO;AAAA,QACL,OAAO;AAAA,UACL;AAAA,UACA,cAAc;AAAA,UACd,YAAY;AAAA,UACZ,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,OAAO;AAAA,QACT;AAAA,QACA,UAAU;AAAA,UACR;AAAA,UACA,QAAQ;AAAA,YACN,KAAK,KAAK;AAAA,YACV,MAAM,KAAK;AAAA,YACX,SAAS,KAAK;AAAA,YACd,OAAO,KAAK;AAAA,YACZ,gBAAgB,KAAK;AAAA,YACrB,YAAY,KAAK;AAAA,YACjB,aAAa,KAAK;AAAA,YAClB,QAAQ,KAAK;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,SAAS,qBAAqB;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth2/provider/wechat.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth2/provider/wechat.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport type { OAuth2Token, Provider } from '../types';\n\nexport type Options = { isWechatBrowser: boolean };\n\n/**\n * reference: https://developers.weixin.qq.com/doc/offiaccount/en/OA_Web_Apps/Wechat_webpage_authorization.html\n */\nexport function createWechatProvider(options: Options = { isWechatBrowser: false }): Provider {\n const { isWechatBrowser } = options;\n return {\n authorizationUri: isWechatBrowser\n ? 'https://open.weixin.qq.com/connect/oauth2/authorize'\n : 'https://open.weixin.qq.com/connect/qrconnect',\n tokenUri: 'https://api.weixin.qq.com/sns/oauth2/access_token',\n tokenRefreshUri: 'https://api.weixin.qq.com/sns/oauth2/refresh_token',\n userInfoUri: 'https://api.weixin.qq.com/sns/userinfo',\n defaultScope: [isWechatBrowser ? 'snsapi_userinfo' : 'snsapi_login'],\n createAuthorizationUri(params) {\n const url = new URL(this.authorizationUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('scope', (params.scope ?? this.defaultScope).join(','));\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('state', params.state);\n return url;\n },\n async exchangeAuthorizationCode(params) {\n const url = new URL(this.tokenUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('secret', params.clientSecret);\n url.searchParams.set('code', params.code);\n url.searchParams.set('grant_type', 'authorization_code');\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n async getUserInfo({ access_token, openid }) {\n invariant(this.userInfoUri, 'userInfoUri is required');\n invariant(openid && typeof openid === 'string', 'openid is required');\n const url = new URL(this.userInfoUri);\n url.searchParams.set('access_token', access_token);\n url.searchParams.set('openid', openid);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n const data = (await response.json()) as WechatUserInfo;\n\n return {\n data,\n claims: {\n sub: data.unionid ?? data.openid,\n name: data.nickname,\n picture: data.headimgurl.split('/').slice(0, -1).join('/') + '/0',\n gender: data.sex === 1 ? 'male' : data.sex === 2 ? 'female' : undefined,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const url = new URL(this.tokenRefreshUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('grant_type', 'refresh_token');\n url.searchParams.set('refresh_token', params.refreshToken);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n };\n}\n\nexport const wechat = createWechatProvider();\n\nexport interface WechatUserInfo {\n openid: string;\n nickname: string;\n sex: 0 | 1 | 2; // 0: unknown, 1: male, 2: female\n province: string;\n city: string;\n country: string;\n /**\n * User's profile photo. The last numeric value represents the size of a square profile photo\n * (The value can be 0, 46, 64, 96, or 132. The value 0 represents a 640*640 square profile\n * photo). This parameter is left blank if a user has no profile photo. If the user changes the\n * profile photo, the URL of the original profile photo will expire.\n * e.g. http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46\n */\n headimgurl: string;\n privilege: string[];\n unionid?: string;\n}\n\nexport interface WechatToken extends OAuth2Token {\n access_token: string;\n expires_in: number;\n refresh_token: string;\n openid: string;\n scope: string;\n unionid?: string;\n}\n\ninterface WechatErrorResponse {\n errcode: number;\n errmsg: string;\n}\n"],"mappings":"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","names":["invariant"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth2/provider/wechat.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth2/provider/wechat.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport type { OAuth2Token, Provider } from '../types';\n\nexport type Options = { isWechatBrowser: boolean };\n\n/**\n * reference: https://developers.weixin.qq.com/doc/offiaccount/en/OA_Web_Apps/Wechat_webpage_authorization.html\n */\nexport function createWechatProvider(options: Options = { isWechatBrowser: false }): Provider {\n const { isWechatBrowser } = options;\n return {\n authorizationUri: isWechatBrowser\n ? 'https://open.weixin.qq.com/connect/oauth2/authorize'\n : 'https://open.weixin.qq.com/connect/qrconnect',\n tokenUri: 'https://api.weixin.qq.com/sns/oauth2/access_token',\n tokenRefreshUri: 'https://api.weixin.qq.com/sns/oauth2/refresh_token',\n userInfoUri: 'https://api.weixin.qq.com/sns/userinfo',\n defaultScope: [isWechatBrowser ? 'snsapi_userinfo' : 'snsapi_login'],\n createAuthorizationUri(params) {\n const url = new URL(this.authorizationUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('scope', (params.scope ?? this.defaultScope).join(','));\n url.searchParams.set('redirect_uri', params.redirectUri);\n url.searchParams.set('response_type', 'code');\n url.searchParams.set('state', params.state);\n return url;\n },\n async exchangeAuthorizationCode(params) {\n const url = new URL(this.tokenUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('secret', params.clientSecret);\n url.searchParams.set('code', params.code);\n url.searchParams.set('grant_type', 'authorization_code');\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n async getUserInfo({ access_token, openid }) {\n invariant(this.userInfoUri, 'userInfoUri is required');\n invariant(openid && typeof openid === 'string', 'openid is required');\n const url = new URL(this.userInfoUri);\n url.searchParams.set('access_token', access_token);\n url.searchParams.set('openid', openid);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n const data = (await response.json()) as WechatUserInfo;\n\n return {\n data,\n claims: {\n sub: data.unionid ?? data.openid,\n name: data.nickname,\n picture: data.headimgurl.split('/').slice(0, -1).join('/') + '/0',\n gender: data.sex === 1 ? 'male' : data.sex === 2 ? 'female' : undefined,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const url = new URL(this.tokenRefreshUri);\n url.searchParams.set('appid', params.clientId);\n url.searchParams.set('grant_type', 'refresh_token');\n url.searchParams.set('refresh_token', params.refreshToken);\n const response = await fetch(url.href);\n if (!response.ok) {\n const { errcode, errmsg } = (await response.json()) as WechatErrorResponse;\n throw new OAuth2Error(response.status, errmsg, `errcode: ${errcode}`);\n }\n return (await response.json()) as WechatToken;\n },\n };\n}\n\nexport const wechat = createWechatProvider();\n\nexport interface WechatUserInfo {\n openid: string;\n nickname: string;\n sex: 0 | 1 | 2; // 0: unknown, 1: male, 2: female\n province: string;\n city: string;\n country: string;\n /**\n * User's profile photo. The last numeric value represents the size of a square profile photo\n * (The value can be 0, 46, 64, 96, or 132. The value 0 represents a 640*640 square profile\n * photo). This parameter is left blank if a user has no profile photo. If the user changes the\n * profile photo, the URL of the original profile photo will expire.\n * e.g. http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46\n */\n headimgurl: string;\n privilege: string[];\n unionid?: string;\n}\n\nexport interface WechatToken extends OAuth2Token {\n access_token: string;\n expires_in: number;\n refresh_token: string;\n openid: string;\n scope: string;\n unionid?: string;\n}\n\ninterface WechatErrorResponse {\n errcode: number;\n errmsg: string;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth2/provider/x.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth2/provider/x.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { OAuth2Token, Provider } from '../types';\nimport {\n createAuthorizationUri,\n exchangeAuthorizationCode,\n refreshAccessToken,\n revokeToken,\n} from './common';\n\nexport interface XOptions {\n userFields?: UserField[];\n}\n\nconst defaultUserFields = [\n 'id',\n 'name',\n 'profile_image_url',\n 'url',\n 'username',\n 'location',\n 'verified',\n];\n\nexport function createXProvider(options?: XOptions): Provider {\n return {\n authorizationUri: 'https://x.com/i/oauth2/authorize',\n tokenUri: 'https://api.x.com/2/oauth2/token',\n tokenRefreshUri: 'https://api.x.com/2/oauth2/token',\n tokenRevokeUri: 'https://api.x.com/2/oauth2/revoke',\n // https://docs.x.com/x-api/users/user-lookup-me\n userInfoUri: 'https://api.x.com/2/users/me',\n defaultScope: ['users.read', 'tweet.read', 'offline.access'],\n createAuthorizationUri(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n const response = await exchangeAuthorizationCode({\n ...params,\n tokenUri: this.tokenUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n\n const userFields = options?.userFields ?? defaultUserFields;\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n\n const url = new URL(this.userInfoUri);\n userFields.forEach((field) => url.searchParams.append('user.fields', field));\n\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n const { errors } = (await response.json()) as XAPIErrorResponse;\n throw new OAuth2Error(\n response.status,\n 'invalid_request',\n 'Failed to fetch user info: ' + (errors.at(0)?.detail ?? errors.at(0)?.title)\n );\n }\n const profile = (await response.json()) as XUserInfo;\n return {\n data: profile,\n claims: {\n sub: profile.data.id,\n name: profile.data.name,\n picture: profile.data.profile_image_url,\n email: profile.data.email,\n email_verified: profile.data.verified || false,\n preferred_username: profile.data.username,\n profile: `https://x.com/${profile.data.username}`,\n website: profile.data.url,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const response = await refreshAccessToken({\n ...params,\n tokenUri: this.tokenRefreshUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async revokeToken(params) {\n invariant(this.tokenRevokeUri, 'tokenRevokeUri is required');\n return revokeToken({\n ...params,\n tokenRevokeUri: this.tokenRevokeUri,\n authentication: 'basic',\n });\n },\n };\n}\n\nexport const x = createXProvider();\n\ntype UserField =\n | 'affiliation'\n | 'connection_status'\n | 'created_at'\n | 'description'\n | 'entities'\n | 'id'\n | 'is_identity_verified'\n | 'location'\n | 'most_recent_tweet_id'\n | 'name'\n | 'parody'\n | 'pinned_tweet_id'\n | 'profile_banner_url'\n | 'profile_image_url'\n | 'protected'\n | 'public_metrics'\n | 'receives_your_dm'\n | 'subscription'\n | 'subscription_type'\n | 'url'\n | 'username'\n | 'verified'\n | 'verified_followers_count'\n | 'verified_type'\n | 'withheld';\n\nexport interface XUser {\n id: string;\n name: string;\n email?: string;\n username: string;\n location?: string;\n entities?: {\n url: { urls: UrlEntity[] };\n description: {\n hashtags: HashtagEntity[];\n cashtags: HashtagEntity[];\n mentions: MentionEntity[];\n };\n };\n verified?: boolean;\n description?: string;\n url?: string;\n profile_image_url?: string;\n protected?: boolean;\n pinned_tweet_id?: string;\n created_at?: string;\n}\n\nexport interface XUserInfo {\n data: XUser;\n includes?: { tweets?: { id: string; text: string }[]; users?: XUser[] };\n}\n\n// prettier-ignore\ntype UrlEntity = { start: number; end: number; url: string; display_url: string; expanded_url: string; }\ntype HashtagEntity = { start: number; end: number; tag: string };\ntype MentionEntity = { start: number; end: string; username: string };\n\nexport interface XToken extends OAuth2Token {\n token_type: string;\n expires_in: number;\n access_token: string;\n scope: string;\n refresh_token: string;\n}\n\ntype XErrorResponse = { error: string; error_description: string };\ntype XAPIErrorResponse = {\n errors: { title: string; type: string; detail?: string; status?: number }[];\n};\n"],"mappings":"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","names":["invariant"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/oauth2/provider/x.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/oauth2/provider/x.ts"],"sourcesContent":["import invariant from 'tiny-invariant';\nimport { OAuth2Error } from '../error';\nimport { OAuth2Token, Provider } from '../types';\nimport {\n createAuthorizationUri,\n exchangeAuthorizationCode,\n refreshAccessToken,\n revokeToken,\n} from './common';\n\nexport interface XOptions {\n userFields?: UserField[];\n}\n\nconst defaultUserFields = [\n 'id',\n 'name',\n 'profile_image_url',\n 'url',\n 'username',\n 'location',\n 'verified',\n];\n\nexport function createXProvider(options?: XOptions): Provider {\n return {\n authorizationUri: 'https://x.com/i/oauth2/authorize',\n tokenUri: 'https://api.x.com/2/oauth2/token',\n tokenRefreshUri: 'https://api.x.com/2/oauth2/token',\n tokenRevokeUri: 'https://api.x.com/2/oauth2/revoke',\n // https://docs.x.com/x-api/users/user-lookup-me\n userInfoUri: 'https://api.x.com/2/users/me',\n defaultScope: ['users.read', 'tweet.read', 'offline.access'],\n createAuthorizationUri(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n return createAuthorizationUri({\n ...params,\n scope: params.scope ?? this.defaultScope,\n authorizationUri: this.authorizationUri,\n });\n },\n async exchangeAuthorizationCode(params) {\n if (!params.pkce) throw new Error('pkce is required for x');\n const response = await exchangeAuthorizationCode({\n ...params,\n tokenUri: this.tokenUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async getUserInfo({ access_token }) {\n invariant(access_token, 'access_token is required');\n invariant(this.userInfoUri, 'userInfoUri is required');\n\n const userFields = options?.userFields ?? defaultUserFields;\n const headers = { Accept: 'application/json', Authorization: `Bearer ${access_token}` };\n\n const url = new URL(this.userInfoUri);\n userFields.forEach((field) => url.searchParams.append('user.fields', field));\n\n const response = await fetch(this.userInfoUri, { headers });\n if (!response.ok) {\n const { errors } = (await response.json()) as XAPIErrorResponse;\n throw new OAuth2Error(\n response.status,\n 'invalid_request',\n 'Failed to fetch user info: ' + (errors.at(0)?.detail ?? errors.at(0)?.title)\n );\n }\n const profile = (await response.json()) as XUserInfo;\n return {\n data: profile,\n claims: {\n sub: profile.data.id,\n name: profile.data.name,\n picture: profile.data.profile_image_url,\n email: profile.data.email,\n email_verified: profile.data.verified || false,\n preferred_username: profile.data.username,\n profile: `https://x.com/${profile.data.username}`,\n website: profile.data.url,\n },\n };\n },\n async refreshAccessToken(params) {\n invariant(this.tokenRefreshUri, 'tokenRefreshUri is required');\n const response = await refreshAccessToken({\n ...params,\n tokenUri: this.tokenRefreshUri,\n authentication: 'basic',\n });\n if (!response.ok) {\n const { error, error_description } = (await response.json()) as XErrorResponse;\n throw new OAuth2Error(response.status, error, error_description);\n }\n return (await response.json()) as XToken;\n },\n async revokeToken(params) {\n invariant(this.tokenRevokeUri, 'tokenRevokeUri is required');\n return revokeToken({\n ...params,\n tokenRevokeUri: this.tokenRevokeUri,\n authentication: 'basic',\n });\n },\n };\n}\n\nexport const x = createXProvider();\n\ntype UserField =\n | 'affiliation'\n | 'connection_status'\n | 'created_at'\n | 'description'\n | 'entities'\n | 'id'\n | 'is_identity_verified'\n | 'location'\n | 'most_recent_tweet_id'\n | 'name'\n | 'parody'\n | 'pinned_tweet_id'\n | 'profile_banner_url'\n | 'profile_image_url'\n | 'protected'\n | 'public_metrics'\n | 'receives_your_dm'\n | 'subscription'\n | 'subscription_type'\n | 'url'\n | 'username'\n | 'verified'\n | 'verified_followers_count'\n | 'verified_type'\n | 'withheld';\n\nexport interface XUser {\n id: string;\n name: string;\n email?: string;\n username: string;\n location?: string;\n entities?: {\n url: { urls: UrlEntity[] };\n description: {\n hashtags: HashtagEntity[];\n cashtags: HashtagEntity[];\n mentions: MentionEntity[];\n };\n };\n verified?: boolean;\n description?: string;\n url?: string;\n profile_image_url?: string;\n protected?: boolean;\n pinned_tweet_id?: string;\n created_at?: string;\n}\n\nexport interface XUserInfo {\n data: XUser;\n includes?: { tweets?: { id: string; text: string }[]; users?: XUser[] };\n}\n\n// prettier-ignore\ntype UrlEntity = { start: number; end: number; url: string; display_url: string; expanded_url: string; }\ntype HashtagEntity = { start: number; end: number; tag: string };\ntype MentionEntity = { start: number; end: string; username: string };\n\nexport interface XToken extends OAuth2Token {\n token_type: string;\n expires_in: number;\n access_token: string;\n scope: string;\n refresh_token: string;\n}\n\ntype XErrorResponse = { error: string; error_description: string };\ntype XAPIErrorResponse = {\n errors: { title: string; type: string; detail?: string; status?: number }[];\n};\n"],"mappings":"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","names":[]}
|
package/dist/session/db.cjs
CHANGED
|
@@ -24,8 +24,8 @@ __export(db_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(db_exports);
|
|
26
26
|
var import_crypto = require("crypto");
|
|
27
|
-
var import_map_session = require("./map-session.cjs");
|
|
28
27
|
var import_common = require("./common.cjs");
|
|
28
|
+
var import_map_session = require("./map-session.cjs");
|
|
29
29
|
var DBSession = class extends import_map_session.MapSession {
|
|
30
30
|
isNew;
|
|
31
31
|
primaryId;
|
package/dist/session/db.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/session/db.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/session/db.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport { PRINCIPAL_NAME_INDEX_NAME, resolveIndexesFor } from './common';\nimport { MapSession } from './map-session';\nimport type { SessionRepository } from './types';\n\ninterface SessionEntity {\n primaryId: string;\n sessionId: string;\n creationTime: number;\n lastAccessTime: number;\n maxInactiveInterval: number;\n expiryTime: number;\n attributes: Record<string, any>;\n principalName?: string | null;\n}\n\nclass DBSession extends MapSession {\n public isNew: boolean;\n public primaryId: string;\n\n constructor(cached: MapSession, isNew: boolean) {\n super(cached);\n this.isNew = isNew;\n this.primaryId = randomUUID();\n }\n}\n\nexport interface DBAdapter {\n save(session: SessionEntity): Promise<void>;\n deleteById(sessionId: string): Promise<void>;\n findById(sessionId: string): Promise<SessionEntity | null>;\n findByPrincipalName(principalName: string): Promise<SessionEntity[]>;\n cleanupExpiredSessions(cleanupCount?: number): Promise<void>;\n}\n\nexport class DBIndexedSessionRepository implements SessionRepository<DBSession> {\n private defaultMaxInactiveInterval = 1800;\n private db: DBAdapter;\n\n constructor(db: DBAdapter) {\n this.db = db;\n }\n\n setDefaultMaxInactiveInterval(interval: number) {\n this.defaultMaxInactiveInterval = interval;\n }\n\n private mapSessionToEntity(session: DBSession): SessionEntity {\n const attributes = new Map<string, string | number | null>();\n for (const name of session.getAttributeNames()) {\n attributes.set(name, session.getAttribute(name));\n }\n const principalName = resolveIndexesFor(session);\n\n return {\n primaryId: session.primaryId,\n sessionId: session.getId(),\n creationTime: session.getCreationTime(),\n lastAccessTime: session.getLastAccessedTime(),\n maxInactiveInterval: session.getMaxInactiveInterval(),\n expiryTime: session.getLastAccessedTime() + session.getMaxInactiveInterval() * 1000,\n attributes,\n principalName,\n };\n }\n\n private mapEntityToSession(entity: SessionEntity, allowExpired: boolean): DBSession | null {\n const loaded = new MapSession(entity.sessionId);\n loaded.setCreationTime(entity.creationTime);\n loaded.setLastAccessedTime(entity.lastAccessTime);\n loaded.setMaxInactiveInterval(entity.maxInactiveInterval);\n for (const [key, value] of Object.entries(entity.attributes)) {\n loaded.setAttribute(key, value);\n }\n\n if (!allowExpired && loaded.isExpired()) return null;\n const session = new DBSession(loaded, false);\n session.primaryId = entity.primaryId;\n return session;\n }\n\n private async getSession(id: string, allowExpired: boolean): Promise<DBSession | null> {\n const entity = await this.db.findById(id);\n if (!entity) return null;\n return this.mapEntityToSession(entity, allowExpired);\n }\n\n // override\n createSession() {\n const cached = new MapSession();\n cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);\n return new DBSession(cached, true);\n }\n\n async save(session: DBSession) {\n const entity = this.mapSessionToEntity(session);\n await this.db.save(entity);\n session.isNew = false;\n }\n\n async findById(sessionId: string): Promise<DBSession | null> {\n return this.getSession(sessionId, false);\n }\n\n async deleteById(sessionId: string) {\n this.db.deleteById(sessionId);\n }\n\n async findByIndexNameAndIndexValue(\n indexName: string,\n indexValue: string\n ): Promise<Map<string, DBSession>> {\n if (indexName !== PRINCIPAL_NAME_INDEX_NAME) {\n return new Map<string, DBSession>();\n }\n const entities = await this.db.findByPrincipalName(indexValue);\n if (entities.length === 0) {\n return new Map<string, DBSession>();\n }\n const sessions = new Map<string, DBSession>();\n for (const entity of entities) {\n const session = this.mapEntityToSession(entity, false);\n if (session) {\n sessions.set(session.getId(), session);\n }\n }\n return sessions;\n }\n\n async findByPrincipalName(principalName: string): Promise<Map<string, DBSession>> {\n return this.findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);\n }\n\n async cleanupExpiredSessions(cleanupCount?: number): Promise<void> {\n return this.db.cleanupExpiredSessions(cleanupCount);\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/session/db.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/session/db.ts
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
|
-
import { MapSession } from "./map-session.mjs";
|
|
4
3
|
import { PRINCIPAL_NAME_INDEX_NAME, resolveIndexesFor } from "./common.mjs";
|
|
4
|
+
import { MapSession } from "./map-session.mjs";
|
|
5
5
|
var DBSession = class extends MapSession {
|
|
6
6
|
isNew;
|
|
7
7
|
primaryId;
|
package/dist/session/db.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/session/db.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/session/db.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport { PRINCIPAL_NAME_INDEX_NAME, resolveIndexesFor } from './common';\nimport { MapSession } from './map-session';\nimport type { SessionRepository } from './types';\n\ninterface SessionEntity {\n primaryId: string;\n sessionId: string;\n creationTime: number;\n lastAccessTime: number;\n maxInactiveInterval: number;\n expiryTime: number;\n attributes: Record<string, any>;\n principalName?: string | null;\n}\n\nclass DBSession extends MapSession {\n public isNew: boolean;\n public primaryId: string;\n\n constructor(cached: MapSession, isNew: boolean) {\n super(cached);\n this.isNew = isNew;\n this.primaryId = randomUUID();\n }\n}\n\nexport interface DBAdapter {\n save(session: SessionEntity): Promise<void>;\n deleteById(sessionId: string): Promise<void>;\n findById(sessionId: string): Promise<SessionEntity | null>;\n findByPrincipalName(principalName: string): Promise<SessionEntity[]>;\n cleanupExpiredSessions(cleanupCount?: number): Promise<void>;\n}\n\nexport class DBIndexedSessionRepository implements SessionRepository<DBSession> {\n private defaultMaxInactiveInterval = 1800;\n private db: DBAdapter;\n\n constructor(db: DBAdapter) {\n this.db = db;\n }\n\n setDefaultMaxInactiveInterval(interval: number) {\n this.defaultMaxInactiveInterval = interval;\n }\n\n private mapSessionToEntity(session: DBSession): SessionEntity {\n const attributes = new Map<string, string | number | null>();\n for (const name of session.getAttributeNames()) {\n attributes.set(name, session.getAttribute(name));\n }\n const principalName = resolveIndexesFor(session);\n\n return {\n primaryId: session.primaryId,\n sessionId: session.getId(),\n creationTime: session.getCreationTime(),\n lastAccessTime: session.getLastAccessedTime(),\n maxInactiveInterval: session.getMaxInactiveInterval(),\n expiryTime: session.getLastAccessedTime() + session.getMaxInactiveInterval() * 1000,\n attributes,\n principalName,\n };\n }\n\n private mapEntityToSession(entity: SessionEntity, allowExpired: boolean): DBSession | null {\n const loaded = new MapSession(entity.sessionId);\n loaded.setCreationTime(entity.creationTime);\n loaded.setLastAccessedTime(entity.lastAccessTime);\n loaded.setMaxInactiveInterval(entity.maxInactiveInterval);\n for (const [key, value] of Object.entries(entity.attributes)) {\n loaded.setAttribute(key, value);\n }\n\n if (!allowExpired && loaded.isExpired()) return null;\n const session = new DBSession(loaded, false);\n session.primaryId = entity.primaryId;\n return session;\n }\n\n private async getSession(id: string, allowExpired: boolean): Promise<DBSession | null> {\n const entity = await this.db.findById(id);\n if (!entity) return null;\n return this.mapEntityToSession(entity, allowExpired);\n }\n\n // override\n createSession() {\n const cached = new MapSession();\n cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval);\n return new DBSession(cached, true);\n }\n\n async save(session: DBSession) {\n const entity = this.mapSessionToEntity(session);\n await this.db.save(entity);\n session.isNew = false;\n }\n\n async findById(sessionId: string): Promise<DBSession | null> {\n return this.getSession(sessionId, false);\n }\n\n async deleteById(sessionId: string) {\n this.db.deleteById(sessionId);\n }\n\n async findByIndexNameAndIndexValue(\n indexName: string,\n indexValue: string\n ): Promise<Map<string, DBSession>> {\n if (indexName !== PRINCIPAL_NAME_INDEX_NAME) {\n return new Map<string, DBSession>();\n }\n const entities = await this.db.findByPrincipalName(indexValue);\n if (entities.length === 0) {\n return new Map<string, DBSession>();\n }\n const sessions = new Map<string, DBSession>();\n for (const entity of entities) {\n const session = this.mapEntityToSession(entity, false);\n if (session) {\n sessions.set(session.getId(), session);\n }\n }\n return sessions;\n }\n\n async findByPrincipalName(principalName: string): Promise<Map<string, DBSession>> {\n return this.findByIndexNameAndIndexValue(PRINCIPAL_NAME_INDEX_NAME, principalName);\n }\n\n async cleanupExpiredSessions(cleanupCount?: number): Promise<void> {\n return this.db.cleanupExpiredSessions(cleanupCount);\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/session/redis.ts"],"sourcesContent":["import { resolveIndexesFor, PRINCIPAL_NAME_INDEX_NAME, SPRING_SECURITY_CONTEXT } from './common';\nimport { MapSession } from './map-session';\nimport type { Redis } from 'ioredis';\nimport type { KVRepository, Namespace, Session, SessionRepository } from './types';\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\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(cleanupCount?: number): 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 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(cleanupCount: number = 100) {\n const key = this.expirationsKey;\n const score = Date.now();\n const sessionIds = await this.redis.zrevrangebyscore(key, score, 0, 'LIMIT', 0, cleanupCount);\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// <spring:session:sessions:uuid, hash<session>> + 300s\n// <spring:session:sessions:expirations, sorted_set<uuid>>\n// <spring:session:sessions:expires:uuid, string<empty>>: for expire check & event\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(cleanupCount?: number) {\n await this.expirationStore.cleanupExpiredSessions(cleanupCount);\n }\n}\n\nexport class RedisKVRepository implements KVRepository {\n private readonly redis: Redis;\n\n constructor(redis: Redis) {\n this.redis = redis;\n }\n\n async setItem(key: string, value: string, expiresIn?: number) {\n if (expiresIn) {\n await this.redis.set(key, value, 'EX', expiresIn);\n } else {\n await this.redis.set(key, value);\n }\n }\n\n async getItem(key: string): Promise<string | null> {\n return await this.redis.get(key);\n }\n\n async removeItem(key: string) {\n await this.redis.del(key);\n }\n}\n"],"mappings":"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","names":["key"]}
|
|
1
|
+
{"version":3,"sources":["../../src/session/redis.ts"],"sourcesContent":["import { resolveIndexesFor, PRINCIPAL_NAME_INDEX_NAME, SPRING_SECURITY_CONTEXT } from './common';\nimport { MapSession } from './map-session';\nimport type { KVRepository, Namespace, Session, SessionRepository } from './types';\nimport type { Redis } from 'ioredis';\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\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(cleanupCount?: number): 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 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(cleanupCount: number = 100) {\n const key = this.expirationsKey;\n const score = Date.now();\n const sessionIds = await this.redis.zrevrangebyscore(key, score, 0, 'LIMIT', 0, cleanupCount);\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// <spring:session:sessions:uuid, hash<session>> + 300s\n// <spring:session:sessions:expirations, sorted_set<uuid>>\n// <spring:session:sessions:expires:uuid, string<empty>>: for expire check & event\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(cleanupCount?: number) {\n await this.expirationStore.cleanupExpiredSessions(cleanupCount);\n }\n}\n\nexport class RedisKVRepository implements KVRepository {\n private readonly redis: Redis;\n\n constructor(redis: Redis) {\n this.redis = redis;\n }\n\n async setItem(key: string, value: string, expiresIn?: number) {\n if (expiresIn) {\n await this.redis.set(key, value, 'EX', expiresIn);\n } else {\n await this.redis.set(key, value);\n }\n }\n\n async getItem(key: string): Promise<string | null> {\n return await this.redis.get(key);\n }\n\n async removeItem(key: string) {\n await this.redis.del(key);\n }\n}\n"],"mappings":"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","names":["key"]}
|
package/dist/session/redis.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MapSession } from './map-session.cjs';
|
|
2
|
-
import { Redis } from 'ioredis';
|
|
3
2
|
import { SessionRepository, Session, Namespace, KVRepository } from './types.cjs';
|
|
3
|
+
import { Redis } from 'ioredis';
|
|
4
4
|
import '../core/index.cjs';
|
|
5
5
|
|
|
6
6
|
declare class RedisSession implements Session {
|
package/dist/session/redis.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MapSession } from './map-session.js';
|
|
2
|
-
import { Redis } from 'ioredis';
|
|
3
2
|
import { SessionRepository, Session, Namespace, KVRepository } from './types.js';
|
|
3
|
+
import { Redis } from 'ioredis';
|
|
4
4
|
import '../core/index.js';
|
|
5
5
|
|
|
6
6
|
declare class RedisSession implements Session {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/session/redis.ts"],"sourcesContent":["import { resolveIndexesFor, PRINCIPAL_NAME_INDEX_NAME, SPRING_SECURITY_CONTEXT } from './common';\nimport { MapSession } from './map-session';\nimport type { Redis } from 'ioredis';\nimport type { KVRepository, Namespace, Session, SessionRepository } from './types';\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\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(cleanupCount?: number): 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 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(cleanupCount: number = 100) {\n const key = this.expirationsKey;\n const score = Date.now();\n const sessionIds = await this.redis.zrevrangebyscore(key, score, 0, 'LIMIT', 0, cleanupCount);\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// <spring:session:sessions:uuid, hash<session>> + 300s\n// <spring:session:sessions:expirations, sorted_set<uuid>>\n// <spring:session:sessions:expires:uuid, string<empty>>: for expire check & event\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(cleanupCount?: number) {\n await this.expirationStore.cleanupExpiredSessions(cleanupCount);\n }\n}\n\nexport class RedisKVRepository implements KVRepository {\n private readonly redis: Redis;\n\n constructor(redis: Redis) {\n this.redis = redis;\n }\n\n async setItem(key: string, value: string, expiresIn?: number) {\n if (expiresIn) {\n await this.redis.set(key, value, 'EX', expiresIn);\n } else {\n await this.redis.set(key, value);\n }\n }\n\n async getItem(key: string): Promise<string | null> {\n return await this.redis.get(key);\n }\n\n async removeItem(key: string) {\n await this.redis.del(key);\n }\n}\n"],"mappings":";AAAA,SAAS,mBAAmB,2BAA2B,+BAA+B;AACtF,SAAS,kBAAkB;AAI3B,IAAM,oBAAoB;AAC1B,IAAM,yBAAyB;AAC/B,IAAM,4BAA4B;AAClC,IAAM,mBAAmB;AACzB,IAAM,yBAAyB;AAE/B,IAAM,eAAN,MAAsC;AAAA,EACnB;AAAA,EAEV;AAAA,EACA,QAAQ,oBAAI,IAAoC;AAAA,EAChD;AAAA,EACA;AAAA,EACA,yBAAwC;AAAA,EAE/C,YAAY,QAAoB,OAAgB;AAC9C,SAAK,SAAS;AACd,SAAK,QAAQ;AACb,SAAK,oBAAoB,OAAO,MAAM;AACtC,SAAK,wBAAwB,kBAAkB,IAAI;AAEnD,QAAI,OAAO;AACT,WAAK,MAAM,IAAI,mBAAmB,OAAO,gBAAgB,CAAC;AAC1D,WAAK,MAAM,IAAI,wBAAwB,OAAO,oBAAoB,CAAC;AACnE,WAAK,MAAM,IAAI,2BAA2B,OAAO,uBAAuB,CAAC;AACzE,WAAK,OAAO,kBAAkB,EAAE,QAAQ,CAAC,SAAS;AAChD,cAAM,QAAQ,KAAK,OAAO,aAAa,IAAI;AAC3C,YAAI,MAAO,MAAK,MAAM,IAAI,KAAK,sBAAsB,IAAI,GAAG,KAAK;AAAA,MACnE,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,sBAAsB,MAAc;AAClC,WAAO,mBAAmB;AAAA,EAC5B;AAAA;AAAA,EAGA,oBAAoB,kBAA0B;AAC5C,SAAK,OAAO,oBAAoB,gBAAgB;AAChD,SAAK,MAAM,IAAI,wBAAwB,gBAAgB;AAAA,EACzD;AAAA,EAEA,YAAY;AACV,WAAO,KAAK,OAAO,UAAU;AAAA,EAC/B;AAAA,EAEA,kBAAkB;AAChB,WAAO,KAAK,OAAO,gBAAgB;AAAA,EACrC;AAAA,EAEA,QAAQ;AACN,WAAO,KAAK,OAAO,MAAM;AAAA,EAC3B;AAAA,EAEA,kBAAkB;AAChB,WAAO,KAAK,OAAO,gBAAgB;AAAA,EACrC;AAAA,EAEA,sBAAsB;AACpB,WAAO,KAAK,OAAO,oBAAoB;AAAA,EACzC;AAAA,EAEA,uBAAuB,UAAkB;AACvC,SAAK,OAAO,uBAAuB,QAAQ;AAC3C,SAAK,MAAM,IAAI,2BAA2B,QAAQ;AAAA,EACpD;AAAA,EAEA,yBAAyB;AACvB,WAAO,KAAK,OAAO,uBAAuB;AAAA,EAC5C;AAAA,EAEA,aAAa,MAAc;AACzB,WAAO,KAAK,OAAO,aAAa,IAAI;AAAA,EACtC;AAAA,EAEA,oBAAoB;AAClB,WAAO,KAAK,OAAO,kBAAkB;AAAA,EACvC;AAAA,EAEA,aAAa,MAAc,OAAe;AACxC,SAAK,OAAO,aAAa,MAAM,KAAK;AACpC,SAAK,MAAM,IAAI,KAAK,sBAAsB,IAAI,GAAG,KAAK;AAAA,EACxD;AAAA,EAEA,gBAAgB,MAAc;AAC5B,SAAK,OAAO,gBAAgB,IAAI;AAChC,SAAK,MAAM,IAAI,KAAK,sBAAsB,IAAI,GAAG,IAAI;AAAA,EACvD;AACF;AASA,IAAM,uCAAN,MAAkF;AAAA,EAC/D;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,OAAc,WAAsB;AAC9C,SAAK,QAAQ;AACb,SAAK,YAAY;AACjB,SAAK,iBAAiB,GAAG,KAAK,SAAS;AAAA,EACzC;AAAA,EAEA,cAAc,WAAmB;AAC/B,WAAO,GAAG,KAAK,SAAS,aAAa,SAAS;AAAA,EAChD;AAAA,EAEA,MAAM,KAAK,SAAuB;AAChC,UAAM,qBACJ,QAAQ,oBAAoB,IAAI,QAAQ,uBAAuB,IAAI;AACrE,UAAM,KAAK,MAAM,KAAK,KAAK,gBAAgB,oBAAoB,QAAQ,MAAM,CAAC;AAAA,EAChF;AAAA,EAEA,MAAM,OAAO,WAAmB;AAC9B,UAAM,KAAK,MAAM,KAAK,KAAK,gBAAgB,SAAS;AAAA,EACtD;AAAA,EAEA,MAAM,OAAO,SAAuB;AAClC,QAAI,QAAQ,sBAAsB,QAAQ,QAAQ,sBAAsB,QAAQ,MAAM,EAAG;AACzF,UAAM,KAAK,OAAO,QAAQ,iBAAiB;AAC3C,UAAM,KAAK,KAAK,OAAO;AAAA,EACzB;AAAA,EAEA,MAAM,uBAAuB,eAAuB,KAAK;AACvD,UAAM,MAAM,KAAK;AACjB,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,aAAa,MAAM,KAAK,MAAM,iBAAiB,KAAK,OAAO,GAAG,SAAS,GAAG,YAAY;AAC5F,QAAI,WAAW,WAAW,EAAG;AAC7B,eAAW,aAAa,YAAY;AAClC,YAAM,aAAa,KAAK,cAAc,SAAS;AAG/C,YAAM,KAAK,MAAM,OAAO,UAAU;AAAA,IACpC;AAAA,EACF;AACF;AAKO,IAAM,gCAAN,MAA+E;AAAA,EACnE;AAAA,EACA;AAAA,EACA;AAAA,EAET,6BAA6B;AAAA,EAErC,YAAY,OAAc,YAAuB,kBAAkB;AACjE,SAAK,QAAQ;AACb,SAAK,YAAY;AACjB,SAAK,kBAAkB,IAAI,qCAAqC,OAAO,SAAS;AAAA,EAClF;AAAA,EAEA,8BAA8B,UAAkB;AAC9C,SAAK,6BAA6B;AAAA,EACpC;AAAA,EAEQ,cAAc,WAAmB;AACvC,WAAO,GAAG,KAAK,SAAS,aAAa,SAAS;AAAA,EAChD;AAAA,EAEQ,cAAc,WAAmB;AACvC,WAAO,GAAG,KAAK,SAAS,qBAAqB,SAAS;AAAA,EACxD;AAAA,EAEQ,sBAAsB,MAAc;AAC1C,WAAO,mBAAmB;AAAA,EAC5B;AAAA,EAEQ,gBAAgB,eAAuB;AAC7C,WAAO,GAAG,KAAK,SAAS,UAAU,yBAAyB,IAAI,aAAa;AAAA,EAC9E;AAAA,EAEA,MAAc,WAAW,IAAY,cAAqD;AACxF,UAAM,MAAM,KAAK,cAAc,EAAE;AACjC,UAAM,UAAU,MAAM,KAAK,MAAM,QAAQ,GAAG;AAC5C,QAAI,CAAC,WAAW,OAAO,KAAK,OAAO,EAAE,WAAW,EAAG,QAAO;AAC1D,UAAM,SAAS,IAAI,WAAW,EAAE;AAChC,QAAI,CAAC,QAAQ,aAAc,OAAM,IAAI,MAAM,kCAAkC;AAC7E,QAAI,CAAC,QAAQ,iBAAkB,OAAM,IAAI,MAAM,sCAAsC;AACrF,QAAI,CAAC,QAAQ,oBAAqB,OAAM,IAAI,MAAM,yCAAyC;AAE3F,WAAO,gBAAgB,OAAO,QAAQ,YAAY,CAAC;AACnD,WAAO,oBAAoB,OAAO,QAAQ,gBAAgB,CAAC;AAC3D,WAAO,uBAAuB,OAAO,QAAQ,mBAAmB,CAAC;AAEjE,eAAW,CAACA,MAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,UAAIA,KAAI,WAAW,gBAAgB,GAAG;AACpC,eAAO,aAAaA,KAAI,MAAM,iBAAiB,MAAM,GAAG,KAAK;AAAA,MAC/D;AAAA,IACF;AAEA,QAAI,CAAC,gBAAgB,OAAO,UAAU,EAAG,QAAO;AAChD,UAAM,SAAS,IAAI,aAAa,QAAQ,KAAK;AAC7C,WAAO,yBAAyB,OAAO,oBAAoB;AAC3D,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,UAAU,SAAuB;AAC7C,QAAI,QAAQ,MAAM,SAAS,EAAG;AAC9B,UAAM,YAAY,QAAQ,MAAM;AAChC,UAAM,QAAQ,oBAAI,IAA6B;AAC/C,UAAM,UAAoB,CAAC;AAC3B,YAAQ,MAAM,QAAQ,CAAC,OAAO,QAAQ;AACpC,UAAI,UAAU,KAAM,OAAM,IAAI,KAAK,KAAK;AAAA,UACnC,SAAQ,KAAK,GAAG;AAAA,IACvB,CAAC;AAED,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,KAAK,MAAM,KAAK,KAAK,cAAc,SAAS,GAAG,KAAK;AAAA,IAC5D;AACA,QAAI,QAAQ,WAAW,GAAG;AACxB,YAAM,KAAK,MAAM,KAAK,KAAK,cAAc,SAAS,GAAG,GAAG,OAAO;AAAA,IACjE;AAEA,UAAM,sBAAsB,KAAK,sBAAsB,yBAAyB;AAChF,UAAM,8BAA8B,KAAK,sBAAsB,uBAAuB;AACtF,QAAI,QAAQ,MAAM,IAAI,mBAAmB,KAAK,QAAQ,MAAM,IAAI,2BAA2B,GAAG;AAC5F,UAAI,QAAQ,0BAA0B,MAAM;AAC1C,cAAM,4BAA4B,KAAK,gBAAgB,QAAQ,qBAAqB;AACpF,cAAM,KAAK,MAAM,KAAK,2BAA2B,SAAS;AAAA,MAC5D;AACA,YAAM,YAAY,kBAAkB,OAAO;AAC3C,cAAQ,wBAAwB;AAChC,UAAI,cAAc,MAAM;AACtB,cAAM,oBAAoB,KAAK,gBAAgB,SAAS;AACxD,cAAM,KAAK,MAAM,KAAK,mBAAmB,SAAS;AAAA,MACpD;AAAA,IACF;AAEA,QAAI,QAAQ,OAAO;AAEjB,cAAQ,QAAQ;AAAA,IAClB;AAEA,UAAM,yBAAyB,QAAQ,uBAAuB;AAG9D,UAAM,cAAc,yBAAyB,QAAQ,MAAM;AAC3D,UAAM,aAAa,KAAK,cAAc,WAAW;AAEjD,QAAI,yBAAyB,GAAG;AAC9B,YAAM,KAAK,MAAM,OAAO,YAAY,EAAE;AACtC,YAAM,KAAK,MAAM,QAAQ,UAAU;AACnC,YAAM,KAAK,MAAM,QAAQ,KAAK,cAAc,QAAQ,MAAM,CAAC,CAAC;AAAA,IAC9D,WAAW,2BAA2B,GAAG;AACvC,YAAM,KAAK,MAAM,IAAI,UAAU;AAAA,IACjC,OAAO;AACL,YAAM,KAAK,MAAM,OAAO,YAAY,EAAE;AACtC,YAAM,KAAK,MAAM,OAAO,YAAY,sBAAsB;AAAA,IAC5D;AAGA,QAAI,yBAAyB,GAAG;AAC9B,YAAM,0BAA0B,yBAAyB,IAAI;AAC7D,YAAM,KAAK,MAAM,OAAO,KAAK,cAAc,QAAQ,MAAM,CAAC,GAAG,uBAAuB;AAAA,IACtF;AAEA,UAAM,KAAK,gBAAgB,KAAK,OAAO;AACvC,YAAQ,MAAM,MAAM;AAAA,EACtB;AAAA,EAEA,MAAc,oBAAoB,SAAuB;AACvD,UAAM,YAAY,QAAQ,MAAM;AAChC,QAAI,cAAc,QAAQ,kBAAmB;AAC7C,QAAI,CAAC,QAAQ,OAAO;AAClB,YAAM,uBAAuB,KAAK,cAAc,QAAQ,iBAAiB;AACzE,YAAM,eAAe,KAAK,cAAc,SAAS;AACjD,YAAM,qBAAqB,KAAK,cAAc,QAAQ,iBAAiB;AACvE,YAAM,aAAa,KAAK,cAAc,SAAS;AAE/C,YAAM,KAAK,MAAM,OAAO,sBAAsB,YAAY;AAC1D,YAAM,KAAK,MAAM,OAAO,oBAAoB,UAAU;AAEtD,UAAI,QAAQ,0BAA0B,MAAM;AAC1C,cAAM,4BAA4B,KAAK,gBAAgB,QAAQ,qBAAqB;AACpF,cAAM,KAAK,MAAM,KAAK,2BAA2B,QAAQ,iBAAiB;AAC1E,cAAM,KAAK,MAAM,KAAK,2BAA2B,SAAS;AAAA,MAC5D;AACA,YAAM,KAAK,gBAAgB,OAAO,OAAO;AAAA,IAC3C;AACA,YAAQ,oBAAoB;AAAA,EAC9B;AAAA;AAAA,EAGA,gBAAgB;AACd,UAAM,SAAS,IAAI,WAAW;AAC9B,WAAO,uBAAuB,KAAK,0BAA0B;AAC7D,WAAO,IAAI,aAAa,QAAQ,IAAI;AAAA,EACtC;AAAA,EAEA,MAAM,KAAK,SAAuB;AAChC,UAAM,KAAK,oBAAoB,OAAO;AACtC,UAAM,KAAK,UAAU,OAAO;AAAA,EAC9B;AAAA,EAEA,MAAM,SAAS,WAAiD;AAC9D,WAAO,KAAK,WAAW,WAAW,KAAK;AAAA,EACzC;AAAA,EAEA,MAAM,WAAW,WAAmB;AAClC,UAAM,UAAU,MAAM,KAAK,WAAW,WAAW,IAAI;AACrD,QAAI,CAAC,QAAS;AAEd,UAAM,YAAY,kBAAkB,OAAO;AAC3C,QAAI,cAAc,MAAM;AACtB,YAAM,KAAK,MAAM,KAAK,KAAK,gBAAgB,SAAS,GAAG,SAAS;AAAA,IAClE;AAEA,UAAM,KAAK,gBAAgB,OAAO,SAAS;AAE3C,UAAM,aAAa,KAAK,cAAc,SAAS;AAC/C,UAAM,KAAK,MAAM,IAAI,UAAU;AAE/B,YAAQ,uBAAuB,CAAC;AAChC,UAAM,KAAK,KAAK,OAAO;AAAA,EACzB;AAAA,EAEA,MAAM,6BAA6B,WAAmB,YAAoB;AACxE,QAAI,cAAc,2BAA2B;AAC3C,aAAO,oBAAI,IAA0B;AAAA,IACvC;AAEA,UAAM,eAAe,KAAK,gBAAgB,UAAU;AACpD,UAAM,aAAa,MAAM,KAAK,MAAM,SAAS,YAAY;AACzD,QAAI,CAAC,cAAc,WAAW,WAAW,GAAG;AAC1C,aAAO,oBAAI,IAA0B;AAAA,IACvC;AACA,UAAM,WAAW,oBAAI,IAA0B;AAC/C,eAAW,aAAa,YAAY;AAClC,YAAM,UAAU,MAAM,KAAK,SAAS,SAAS;AAC7C,UAAI,YAAY,MAAM;AACpB,iBAAS,IAAI,WAAW,OAAO;AAAA,MACjC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,oBAAoB,eAAuB;AAC/C,WAAO,KAAK,6BAA6B,2BAA2B,aAAa;AAAA,EACnF;AAAA,EAEA,MAAM,uBAAuB,cAAuB;AAClD,UAAM,KAAK,gBAAgB,uBAAuB,YAAY;AAAA,EAChE;AACF;AAEO,IAAM,oBAAN,MAAgD;AAAA,EACpC;AAAA,EAEjB,YAAY,OAAc;AACxB,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,QAAQ,KAAa,OAAe,WAAoB;AAC5D,QAAI,WAAW;AACb,YAAM,KAAK,MAAM,IAAI,KAAK,OAAO,MAAM,SAAS;AAAA,IAClD,OAAO;AACL,YAAM,KAAK,MAAM,IAAI,KAAK,KAAK;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,KAAqC;AACjD,WAAO,MAAM,KAAK,MAAM,IAAI,GAAG;AAAA,EACjC;AAAA,EAEA,MAAM,WAAW,KAAa;AAC5B,UAAM,KAAK,MAAM,IAAI,GAAG;AAAA,EAC1B;AACF;","names":["key"]}
|
|
1
|
+
{"version":3,"sources":["../../src/session/redis.ts"],"sourcesContent":["import { resolveIndexesFor, PRINCIPAL_NAME_INDEX_NAME, SPRING_SECURITY_CONTEXT } from './common';\nimport { MapSession } from './map-session';\nimport type { KVRepository, Namespace, Session, SessionRepository } from './types';\nimport type { Redis } from 'ioredis';\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\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(cleanupCount?: number): 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 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(cleanupCount: number = 100) {\n const key = this.expirationsKey;\n const score = Date.now();\n const sessionIds = await this.redis.zrevrangebyscore(key, score, 0, 'LIMIT', 0, cleanupCount);\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// <spring:session:sessions:uuid, hash<session>> + 300s\n// <spring:session:sessions:expirations, sorted_set<uuid>>\n// <spring:session:sessions:expires:uuid, string<empty>>: for expire check & event\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(cleanupCount?: number) {\n await this.expirationStore.cleanupExpiredSessions(cleanupCount);\n }\n}\n\nexport class RedisKVRepository implements KVRepository {\n private readonly redis: Redis;\n\n constructor(redis: Redis) {\n this.redis = redis;\n }\n\n async setItem(key: string, value: string, expiresIn?: number) {\n if (expiresIn) {\n await this.redis.set(key, value, 'EX', expiresIn);\n } else {\n await this.redis.set(key, value);\n }\n }\n\n async getItem(key: string): Promise<string | null> {\n return await this.redis.get(key);\n }\n\n async removeItem(key: string) {\n await this.redis.del(key);\n }\n}\n"],"mappings":"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","names":["key"]}
|