@vunexa/lixa 0.1.6-alpha.2 → 0.1.6-alpha.4
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/README.md +114 -47
- package/README.template.md +113 -17
- package/dist/dao/types.d.ts +47 -0
- package/dist/dao/types.d.ts.map +1 -1
- package/dist/export-types/index.d.ts +95 -5
- package/dist/index.cjs +185 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +94 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +185 -12
- package/dist/index.js.map +1 -1
- package/dist/lixa.d.ts +37 -5
- package/dist/lixa.d.ts.map +1 -1
- package/dist/models/session.d.ts +2 -0
- package/dist/models/session.d.ts.map +1 -1
- package/dist/types.d.ts +11 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -176,6 +176,7 @@ var Lixa = class _Lixa {
|
|
|
176
176
|
config;
|
|
177
177
|
stateHandler;
|
|
178
178
|
sessionHandler;
|
|
179
|
+
resourceHandler;
|
|
179
180
|
debug;
|
|
180
181
|
/**
|
|
181
182
|
* Creates a new Lixa instance with the provided configuration.
|
|
@@ -194,6 +195,7 @@ var Lixa = class _Lixa {
|
|
|
194
195
|
this.config = config;
|
|
195
196
|
this.stateHandler = config.stateHandler || _Lixa.LOCAL_STATE_HANDLER;
|
|
196
197
|
this.sessionHandler = config.sessionHandler || _Lixa.LOCAL_SESSION_HANDLER;
|
|
198
|
+
this.resourceHandler = config.resourceHandler || _Lixa.LOCAL_RESOURCE_HANDLER;
|
|
197
199
|
this.debug = config.debug || false;
|
|
198
200
|
this.log("INFO", "Init", "Initializing Lixa instance", {
|
|
199
201
|
providers: Object.keys(config.providers),
|
|
@@ -873,6 +875,45 @@ var Lixa = class _Lixa {
|
|
|
873
875
|
* @param params - Object containing sessionId, provider, code, state, and requested scopes
|
|
874
876
|
* @returns Updated Session containing stored resource tokens under session.resources[provider]
|
|
875
877
|
*/
|
|
878
|
+
static userResourceStore = /* @__PURE__ */ new Map();
|
|
879
|
+
static LOCAL_RESOURCE_HANDLER = {
|
|
880
|
+
resourceStorage: {
|
|
881
|
+
saveResource: async (userId, provider, resource) => {
|
|
882
|
+
let userMap = _Lixa.userResourceStore.get(userId);
|
|
883
|
+
if (!userMap) {
|
|
884
|
+
userMap = /* @__PURE__ */ new Map();
|
|
885
|
+
_Lixa.userResourceStore.set(userId, userMap);
|
|
886
|
+
}
|
|
887
|
+
userMap.set(provider.toLowerCase(), resource);
|
|
888
|
+
},
|
|
889
|
+
getResource: async (userId, provider) => {
|
|
890
|
+
const userMap = _Lixa.userResourceStore.get(userId);
|
|
891
|
+
return userMap?.get(provider.toLowerCase()) || null;
|
|
892
|
+
},
|
|
893
|
+
getUserResources: async (userId) => {
|
|
894
|
+
const userMap = _Lixa.userResourceStore.get(userId);
|
|
895
|
+
const result = {};
|
|
896
|
+
if (userMap) {
|
|
897
|
+
for (const [p, r] of userMap.entries()) {
|
|
898
|
+
result[p] = r;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
return result;
|
|
902
|
+
},
|
|
903
|
+
deleteResource: async (userId, provider) => {
|
|
904
|
+
const userMap = _Lixa.userResourceStore.get(userId);
|
|
905
|
+
if (userMap) {
|
|
906
|
+
userMap.delete(provider.toLowerCase());
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
getUserKeyFromSession(session) {
|
|
912
|
+
return session.userId || session.email || session.id || "anonymous";
|
|
913
|
+
}
|
|
914
|
+
/**
|
|
915
|
+
* Handles the OAuth callback for a connected resource provider and stores resource tokens bound to user account.
|
|
916
|
+
*/
|
|
876
917
|
async handleResourceCallback(params) {
|
|
877
918
|
const { sessionId, provider, code, state, scopes } = params;
|
|
878
919
|
const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
|
|
@@ -896,44 +937,176 @@ var Lixa = class _Lixa {
|
|
|
896
937
|
}
|
|
897
938
|
}
|
|
898
939
|
const tokens = await this.exchangeCodeForToken(code, providerConfig, providerImpl, codeVerifier);
|
|
899
|
-
|
|
900
|
-
|
|
940
|
+
const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
|
|
941
|
+
const connectedResource = {
|
|
901
942
|
provider: providerType,
|
|
902
943
|
accessToken: tokens.access_token,
|
|
903
944
|
refreshToken: tokens.refresh_token,
|
|
945
|
+
expiresAt,
|
|
904
946
|
scopes: scopes || (tokens.scope ? tokens.scope.split(" ") : []),
|
|
905
947
|
raw: tokens,
|
|
906
948
|
connectedAt: Date.now()
|
|
907
949
|
};
|
|
950
|
+
const userKey = this.getUserKeyFromSession(activeSession);
|
|
951
|
+
const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
|
|
952
|
+
await resourceStorage.saveResource(userKey, providerType, connectedResource);
|
|
953
|
+
activeSession.resources = activeSession.resources || {};
|
|
954
|
+
activeSession.resources[providerType] = connectedResource;
|
|
908
955
|
await sessionStorage.saveSession(sessionId, activeSession, 86400);
|
|
909
956
|
return activeSession;
|
|
910
957
|
}
|
|
911
958
|
/**
|
|
912
|
-
* Retrieves a connected resource
|
|
959
|
+
* Retrieves a connected resource for a specific User ID / Email directly (independent of session IDs).
|
|
960
|
+
* Automatically refreshes expired access tokens if a refresh token is present.
|
|
961
|
+
*
|
|
962
|
+
* @param userIdOrEmail - User identifier or email
|
|
963
|
+
* @param provider - Resource provider identifier (e.g. 'github', 'google')
|
|
964
|
+
*/
|
|
965
|
+
async getUserResource(userIdOrEmail, provider) {
|
|
966
|
+
const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
|
|
967
|
+
const providerType = provider.toLowerCase();
|
|
968
|
+
const resource = await resourceStorage.getResource(userIdOrEmail, providerType);
|
|
969
|
+
if (!resource) return null;
|
|
970
|
+
if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 6e4) {
|
|
971
|
+
this.log("INFO", "Token", `Resource access token for user '${userIdOrEmail}' on '${providerType}' is expired. Auto-refreshing...`);
|
|
972
|
+
try {
|
|
973
|
+
return await this.refreshUserResourceToken(userIdOrEmail, providerType);
|
|
974
|
+
} catch (error) {
|
|
975
|
+
this.log("ERROR", "Token", `Failed to auto-refresh resource token for user '${userIdOrEmail}' on '${providerType}'`, { error: String(error) });
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
return resource;
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Retrieves all connected resources for a specific User ID / Email.
|
|
982
|
+
*/
|
|
983
|
+
async getUserResources(userIdOrEmail) {
|
|
984
|
+
const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
|
|
985
|
+
return await resourceStorage.getUserResources(userIdOrEmail);
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Retrieves a connected resource provider token for an active session, auto-refreshing expired tokens if possible.
|
|
913
989
|
*
|
|
914
990
|
* @param sessionId - Active session ID
|
|
915
|
-
* @param provider - Provider identifier (e.g. 'github')
|
|
991
|
+
* @param provider - Provider identifier (e.g. 'github', 'google')
|
|
916
992
|
*/
|
|
917
993
|
async getConnectedResource(sessionId, provider) {
|
|
918
994
|
const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
|
|
919
995
|
const activeSession = await sessionStorage.getSession(sessionId);
|
|
920
|
-
if (!activeSession
|
|
921
|
-
|
|
996
|
+
if (!activeSession) return null;
|
|
997
|
+
const providerType = provider.toLowerCase();
|
|
998
|
+
const userKey = this.getUserKeyFromSession(activeSession);
|
|
999
|
+
let resource = await this.getUserResource(userKey, providerType);
|
|
1000
|
+
if (!resource && activeSession.resources) {
|
|
1001
|
+
resource = activeSession.resources[providerType] || null;
|
|
1002
|
+
}
|
|
1003
|
+
if (resource) {
|
|
1004
|
+
if (resource.refreshToken && resource.expiresAt && Date.now() >= resource.expiresAt - 6e4) {
|
|
1005
|
+
this.log("INFO", "Token", `Resource access token for user '${userKey}' on '${providerType}' is expired. Auto-refreshing...`);
|
|
1006
|
+
try {
|
|
1007
|
+
resource = await this.refreshResourceToken(sessionId, providerType);
|
|
1008
|
+
} catch (error) {
|
|
1009
|
+
this.log("ERROR", "Token", `Failed to auto-refresh resource token for user '${userKey}' on '${providerType}'`, { error: String(error) });
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
activeSession.resources = activeSession.resources || {};
|
|
1013
|
+
activeSession.resources[providerType] = resource;
|
|
1014
|
+
}
|
|
1015
|
+
return resource;
|
|
922
1016
|
}
|
|
923
1017
|
/**
|
|
924
|
-
*
|
|
1018
|
+
* Refreshes a user's resource access token using its refresh token.
|
|
925
1019
|
*
|
|
926
|
-
* @param
|
|
927
|
-
* @param provider - Provider identifier
|
|
1020
|
+
* @param userIdOrEmail - User identifier or email
|
|
1021
|
+
* @param provider - Provider identifier (e.g. 'google', 'github')
|
|
928
1022
|
*/
|
|
929
|
-
async
|
|
1023
|
+
async refreshUserResourceToken(userIdOrEmail, provider, existingResource) {
|
|
1024
|
+
const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
|
|
1025
|
+
const providerType = provider.toLowerCase();
|
|
1026
|
+
let resource = existingResource || await resourceStorage.getResource(userIdOrEmail, providerType);
|
|
1027
|
+
if (!resource || !resource.refreshToken) {
|
|
1028
|
+
throw new Error(`No refresh token available for user '${userIdOrEmail}' on connected resource '${provider}'`);
|
|
1029
|
+
}
|
|
1030
|
+
const providerConfig = this.findProviderByType(providerType);
|
|
1031
|
+
if (!providerConfig) {
|
|
1032
|
+
throw new Error(`Provider '${provider}' is not configured`);
|
|
1033
|
+
}
|
|
1034
|
+
const providerImpl = this.getProvider(providerType, providerConfig);
|
|
1035
|
+
const body = {
|
|
1036
|
+
client_id: providerConfig.clientId,
|
|
1037
|
+
client_secret: providerConfig.clientSecret,
|
|
1038
|
+
grant_type: "refresh_token",
|
|
1039
|
+
refresh_token: resource.refreshToken
|
|
1040
|
+
};
|
|
1041
|
+
const response = await fetch(providerImpl.tokenEndpoint, {
|
|
1042
|
+
method: "POST",
|
|
1043
|
+
headers: {
|
|
1044
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1045
|
+
Accept: "application/json"
|
|
1046
|
+
},
|
|
1047
|
+
body: new URLSearchParams(body).toString()
|
|
1048
|
+
});
|
|
1049
|
+
if (!response.ok) {
|
|
1050
|
+
const errorText = await response.text();
|
|
1051
|
+
throw new Error(`Failed to refresh resource token for user '${userIdOrEmail}' on '${provider}': ${response.status} - ${errorText}`);
|
|
1052
|
+
}
|
|
1053
|
+
const tokens = await response.json();
|
|
1054
|
+
const expiresAt = tokens.expires_in ? Date.now() + tokens.expires_in * 1e3 : void 0;
|
|
1055
|
+
resource.accessToken = tokens.access_token;
|
|
1056
|
+
if (tokens.refresh_token) {
|
|
1057
|
+
resource.refreshToken = tokens.refresh_token;
|
|
1058
|
+
}
|
|
1059
|
+
if (expiresAt) {
|
|
1060
|
+
resource.expiresAt = expiresAt;
|
|
1061
|
+
}
|
|
1062
|
+
resource.raw = tokens;
|
|
1063
|
+
resource.connectedAt = Date.now();
|
|
1064
|
+
await resourceStorage.saveResource(userIdOrEmail, providerType, resource);
|
|
1065
|
+
return resource;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Refreshes a connected resource access token for an active session.
|
|
1069
|
+
*/
|
|
1070
|
+
async refreshResourceToken(sessionId, provider) {
|
|
930
1071
|
const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
|
|
931
1072
|
const activeSession = await sessionStorage.getSession(sessionId);
|
|
932
|
-
if (!activeSession
|
|
933
|
-
|
|
1073
|
+
if (!activeSession) {
|
|
1074
|
+
throw new Error("Active session not found.");
|
|
1075
|
+
}
|
|
1076
|
+
const providerType = provider.toLowerCase();
|
|
1077
|
+
const userKey = this.getUserKeyFromSession(activeSession);
|
|
1078
|
+
const existingResource = activeSession.resources ? activeSession.resources[providerType] : void 0;
|
|
1079
|
+
const refreshed = await this.refreshUserResourceToken(userKey, providerType, existingResource);
|
|
1080
|
+
activeSession.resources = activeSession.resources || {};
|
|
1081
|
+
activeSession.resources[providerType] = refreshed;
|
|
934
1082
|
await sessionStorage.saveSession(sessionId, activeSession, 86400);
|
|
1083
|
+
return refreshed;
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Disconnects a resource provider for a specific User ID / Email.
|
|
1087
|
+
*/
|
|
1088
|
+
async disconnectUserResource(userIdOrEmail, provider) {
|
|
1089
|
+
const resourceStorage = this.resourceHandler.resourceStorage || _Lixa.LOCAL_RESOURCE_HANDLER.resourceStorage;
|
|
1090
|
+
await resourceStorage.deleteResource(userIdOrEmail, provider.toLowerCase());
|
|
935
1091
|
return true;
|
|
936
1092
|
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Disconnects a resource provider from an active session and user account.
|
|
1095
|
+
*/
|
|
1096
|
+
async disconnectResource(sessionId, provider) {
|
|
1097
|
+
const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
|
|
1098
|
+
const activeSession = await sessionStorage.getSession(sessionId);
|
|
1099
|
+
if (activeSession) {
|
|
1100
|
+
const userKey = this.getUserKeyFromSession(activeSession);
|
|
1101
|
+
await this.disconnectUserResource(userKey, provider);
|
|
1102
|
+
if (activeSession.resources) {
|
|
1103
|
+
delete activeSession.resources[provider.toLowerCase()];
|
|
1104
|
+
await sessionStorage.saveSession(sessionId, activeSession, 86400);
|
|
1105
|
+
}
|
|
1106
|
+
return true;
|
|
1107
|
+
}
|
|
1108
|
+
return false;
|
|
1109
|
+
}
|
|
937
1110
|
async fetchSessionInfo(sessionId) {
|
|
938
1111
|
const sessionStorage = this.sessionHandler.sessionStorage || _Lixa.LOCAL_SESSION_HANDLER.sessionStorage;
|
|
939
1112
|
return await sessionStorage.getSession(sessionId);
|