@auth/azure-tables-adapter
Version:
Azure Tables Storage adapter for next-auth.
218 lines (217 loc) • 7.91 kB
JavaScript
/**
* <div style={{display: "flex", justifyContent: "space-between", alignItems: "center", padding: "16px"}}>
* <p>An official <a href="https://azure.microsoft.com/en-us/products/storage/tables">Azure Table Storage</a> adapter for Auth.js / NextAuth.js.</p>
* <a href="https://azure.microsoft.com/en-us/products/storage/tables">
* <img style={{display: "block"}} src="/img/adapters/azure-tables.svg" width="48" />
* </a>
* </div>
*
* ## Installation
*
* ```bash npm2yarn
* npm install next-auth @auth/azure-tables-adapter
* ```
*
* @module @auth/azure-tables-adapter
*/
export const keys = {
user: "user",
userByEmail: "userByEmail",
account: "account",
accountByUserId: "accountByUserId",
session: "session",
sessionByUserId: "sessionByUserId",
verificationToken: "verificationToken",
};
export function withoutKeys(entity) {
delete entity.partitionKey;
delete entity.rowKey;
// @ts-expect-error
delete entity.etag;
delete entity.timestamp;
// @ts-expect-error
delete entity["odata.metadata"];
return entity;
}
export const TableStorageAdapter = (client) => {
return {
async createUser(user) {
const id = crypto.randomUUID();
const newUser = {
...user,
id,
};
await Promise.all([
client.createEntity({
...newUser,
partitionKey: keys.userByEmail,
rowKey: user.email,
}),
client.createEntity({
...newUser,
partitionKey: keys.user,
rowKey: id,
}),
]);
return newUser;
},
async getUser(id) {
try {
const user = await client.getEntity(keys.user, id);
return withoutKeys(user);
}
catch {
return null;
}
},
async getUserByEmail(email) {
try {
const user = await client.getEntity(keys.userByEmail, email);
return withoutKeys(user);
}
catch {
return null;
}
},
async getUserByAccount({ providerAccountId, provider }) {
try {
const rowKey = `${providerAccountId}_${provider}`;
const account = await client.getEntity(keys.account, rowKey);
const user = await client.getEntity(keys.user, account.userId);
return withoutKeys(user);
}
catch {
return null;
}
},
async updateUser(user) {
const _user = await client.getEntity(keys.user, user.id);
const updatedUser = {
...user,
partitionKey: keys.user,
rowKey: _user.id,
};
await client.updateEntity(updatedUser, "Merge");
return { ..._user, ...updatedUser };
},
async deleteUser(userId) {
try {
const user = await client.getEntity(keys.user, userId);
const { sessionToken } = await client.getEntity(keys.sessionByUserId, userId);
const accounts = withoutKeys(await client.getEntity(keys.accountByUserId, userId));
const deleteAccounts = Object.keys(accounts).map((property) => client.deleteEntity(keys.account, `${accounts[property]}_${property}`));
await Promise.allSettled([
client.deleteEntity(keys.userByEmail, user.email),
client.deleteEntity(keys.user, userId),
client.deleteEntity(keys.session, sessionToken),
client.deleteEntity(keys.sessionByUserId, userId),
...deleteAccounts,
client.deleteEntity(keys.accountByUserId, userId),
]);
return withoutKeys(user);
}
catch {
return null;
}
},
async linkAccount(account) {
try {
await client.createEntity({
...account,
partitionKey: keys.account,
rowKey: `${account.providerAccountId}_${account.provider}`,
});
await client.upsertEntity({
partitionKey: keys.accountByUserId,
rowKey: account.userId,
[account.provider]: account.providerAccountId,
});
return account;
}
catch {
return null;
}
},
async unlinkAccount({ providerAccountId, provider }) {
const rowKey = `${providerAccountId}_${provider}`;
const account = await client.getEntity(keys.account, rowKey);
await client.deleteEntity(keys.account, rowKey);
await client.deleteEntity(keys.accountByUserId, account.userId);
},
async createSession(session) {
await client.createEntity({
...session,
partitionKey: keys.session,
rowKey: session.sessionToken,
});
await client.upsertEntity({
partitionKey: keys.sessionByUserId,
rowKey: session.userId,
sessionToken: session.sessionToken,
});
return session;
},
async getSessionAndUser(sessionToken) {
try {
const session = await client.getEntity(keys.session, sessionToken);
if (session.expires.valueOf() < Date.now()) {
await client.deleteEntity(keys.session, sessionToken);
}
const user = await client.getEntity(keys.user, session.userId);
return {
session: withoutKeys(session),
user: withoutKeys(user),
};
}
catch {
return null;
}
},
async updateSession(session) {
const _session = await client.getEntity(keys.session, session.sessionToken);
const newSession = {
expires: session.expires ?? _session.expires,
};
await client.updateEntity({
...newSession,
partitionKey: keys.session,
rowKey: session.sessionToken,
});
return { ...withoutKeys(_session), ...newSession };
},
async deleteSession(sessionToken) {
try {
const session = await client.getEntity(keys.session, sessionToken);
await Promise.allSettled([
client.deleteEntity(keys.session, sessionToken),
client.deleteEntity(keys.sessionByUserId, session.userId),
]);
return withoutKeys(session);
}
catch {
return null;
}
},
async createVerificationToken(token) {
await client.createEntity({
...token,
partitionKey: keys.verificationToken,
rowKey: token.token,
});
return token;
},
async useVerificationToken({ identifier, token }) {
try {
const tokenEntity = await client.getEntity(keys.verificationToken, token);
if (tokenEntity.identifier !== identifier) {
return null;
}
await client.deleteEntity(keys.verificationToken, token);
return withoutKeys(tokenEntity);
}
catch {
return null;
}
},
};
};