mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-03-19 23:28:23 -04:00
Add vault upload mechanism (#541)
This commit is contained in:
@@ -1,14 +1,18 @@
|
||||
import { Vault } from './types/webapi/Vault';
|
||||
import EncryptionUtility from './utils/EncryptionUtility';
|
||||
import SqliteClient from './utils/SqliteClient';
|
||||
import { WebApiService } from './utils/WebApiService';
|
||||
|
||||
let vaultState: {
|
||||
derivedKey: string | null;
|
||||
publicEmailDomains: string[];
|
||||
privateEmailDomains: string[];
|
||||
vaultRevisionNumber: number;
|
||||
} = {
|
||||
derivedKey: null,
|
||||
publicEmailDomains: [],
|
||||
privateEmailDomains: []
|
||||
privateEmailDomains: [],
|
||||
vaultRevisionNumber: 0,
|
||||
};
|
||||
|
||||
// Listen for messages from popup
|
||||
@@ -19,6 +23,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
vaultState.derivedKey = message.derivedKey;
|
||||
vaultState.publicEmailDomains = message.publicEmailDomains || [];
|
||||
vaultState.privateEmailDomains = message.privateEmailDomains || [];
|
||||
vaultState.vaultRevisionNumber = message.vaultRevisionNumber || 0;
|
||||
|
||||
// Re-encrypt vault with session key
|
||||
(async () : Promise<void> => {
|
||||
@@ -32,7 +37,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
chrome.storage.session.set({
|
||||
encryptedVault,
|
||||
publicEmailDomains: vaultState.publicEmailDomains,
|
||||
privateEmailDomains: vaultState.privateEmailDomains
|
||||
privateEmailDomains: vaultState.privateEmailDomains,
|
||||
vaultRevisionNumber: vaultState.vaultRevisionNumber
|
||||
}, () => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.error('Failed to store vault:', chrome.runtime.lastError);
|
||||
@@ -54,7 +60,7 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.storage.session.get(['encryptedVault', 'publicEmailDomains', 'privateEmailDomains'], async (result) => {
|
||||
chrome.storage.session.get(['encryptedVault', 'publicEmailDomains', 'privateEmailDomains', 'vaultRevisionNumber'], async (result) => {
|
||||
try {
|
||||
if (!result.encryptedVault) {
|
||||
console.error('No encrypted vault found in storage');
|
||||
@@ -73,7 +79,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
sendResponse({
|
||||
vault: decryptedVault,
|
||||
publicEmailDomains: result.publicEmailDomains || [],
|
||||
privateEmailDomains: result.privateEmailDomains || []
|
||||
privateEmailDomains: result.privateEmailDomains || [],
|
||||
vaultRevisionNumber: result.vaultRevisionNumber || 0
|
||||
});
|
||||
} catch (parseError) {
|
||||
console.error('Failed to parse decrypted vault:', parseError);
|
||||
@@ -90,7 +97,8 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
vaultState.derivedKey = null;
|
||||
vaultState.publicEmailDomains = [];
|
||||
vaultState.privateEmailDomains = [];
|
||||
chrome.storage.session.remove(['encryptedVault', 'publicEmailDomains', 'privateEmailDomains']);
|
||||
vaultState.vaultRevisionNumber = 0;
|
||||
chrome.storage.session.remove(['encryptedVault', 'publicEmailDomains', 'privateEmailDomains', 'vaultRevisionNumber']);
|
||||
sendResponse({ success: true });
|
||||
break;
|
||||
}
|
||||
@@ -231,6 +239,33 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
privateEmailDomains: vaultState.privateEmailDomains
|
||||
});
|
||||
|
||||
// Upload new vault to server
|
||||
// TODO: extract all required fields by quering the vault db.
|
||||
const username = await chrome.storage.local.get('username');
|
||||
const newVault: Vault = {
|
||||
blob: encryptedVault,
|
||||
createdAt: new Date().toISOString(),
|
||||
credentialsCount: 0, // TODO
|
||||
currentRevisionNumber: vaultState.vaultRevisionNumber,
|
||||
emailAddressList: await getEmailAddressesForVault(sqliteClient),
|
||||
privateEmailDomainList: [], // TODO
|
||||
publicEmailDomainList: [], // TODO
|
||||
encryptionPublicKey: '', // TODO
|
||||
updatedAt: new Date().toISOString(),
|
||||
username: username.username, // TODO
|
||||
version: '1.0.0' // TODO
|
||||
}
|
||||
|
||||
console.log('New vault to upload:', newVault);
|
||||
|
||||
const webApi = new WebApiService(
|
||||
() => {}
|
||||
);
|
||||
await webApi.initializeBaseUrl();
|
||||
await webApi.post('Vault', newVault);
|
||||
|
||||
console.log('Vault uploaded successfully');
|
||||
|
||||
sendResponse({ success: true });
|
||||
} catch (error) {
|
||||
console.error('Failed to create identity:', error);
|
||||
@@ -242,3 +277,26 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get all active email addresses from aliases
|
||||
* @param sqliteClient The SQLite client
|
||||
* @returns The list of email addresses
|
||||
*/
|
||||
async function getEmailAddressesForVault(sqliteClient: SqliteClient): Promise<string[]> {
|
||||
const credentials = sqliteClient.getAllCredentials();
|
||||
|
||||
// Extract unique email addresses from credentials
|
||||
const emailAddresses = credentials
|
||||
.filter(cred => cred.Email != null)
|
||||
.map(cred => cred.Email!)
|
||||
.filter((email, index, self) => self.indexOf(email) === index); // Get unique values
|
||||
|
||||
// Filter to only include domains from the private domains list
|
||||
const filteredEmailAddresses = emailAddresses.filter(email => {
|
||||
const domain = email.split('@')[1];
|
||||
return vaultState.privateEmailDomains.includes(domain);
|
||||
});
|
||||
|
||||
return filteredEmailAddresses;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,7 @@ type AuthContextType = {
|
||||
isInitialized: boolean;
|
||||
username: string | null;
|
||||
login: (username: string, accessToken: string, refreshToken: string) => Promise<void>;
|
||||
updateTokens: (accessToken: string, refreshToken: string) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
getAccessToken: () => string | null;
|
||||
getRefreshToken: () => string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,94 +20,49 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children
|
||||
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const [username, setUsername] = useState<string | null>(null);
|
||||
const [, setAccessToken] = useState<string | null>(null);
|
||||
const [, setRefreshToken] = useState<string | null>(null);
|
||||
const accessTokenRef = useRef<string | null>(null);
|
||||
const refreshTokenRef = useRef<string | null>(null);
|
||||
|
||||
/**
|
||||
* Check for tokens in localStorage on initial load
|
||||
* Check for tokens in chrome storage on initial load
|
||||
*/
|
||||
useEffect(() : void => {
|
||||
const storedAccessToken = localStorage.getItem('accessToken');
|
||||
const storedRefreshToken = localStorage.getItem('refreshToken');
|
||||
const storedUsername = localStorage.getItem('username');
|
||||
if (storedAccessToken && storedRefreshToken && storedUsername) {
|
||||
setAccessToken(storedAccessToken);
|
||||
setRefreshToken(storedRefreshToken);
|
||||
setUsername(storedUsername);
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
useEffect(() => {
|
||||
const initializeAuth = async () => {
|
||||
const stored = await chrome.storage.local.get(['accessToken', 'refreshToken', 'username']);
|
||||
if (stored.accessToken && stored.refreshToken && stored.username) {
|
||||
setUsername(stored.username);
|
||||
setIsLoggedIn(true);
|
||||
}
|
||||
setIsInitialized(true);
|
||||
};
|
||||
|
||||
setIsInitialized(true);
|
||||
initializeAuth();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Login
|
||||
*/
|
||||
const login = async (username: string, accessToken: string, refreshToken: string) : Promise<void> => {
|
||||
accessTokenRef.current = accessToken; // Immediate update
|
||||
refreshTokenRef.current = refreshToken; // Immediate update
|
||||
await Promise.all([
|
||||
localStorage.setItem('username', username),
|
||||
localStorage.setItem('accessToken', accessToken),
|
||||
localStorage.setItem('refreshToken', refreshToken),
|
||||
]);
|
||||
const login = async (username: string, accessToken: string, refreshToken: string) => {
|
||||
await chrome.storage.local.set({
|
||||
username,
|
||||
accessToken,
|
||||
refreshToken
|
||||
});
|
||||
|
||||
setUsername(username);
|
||||
setAccessToken(accessToken);
|
||||
setRefreshToken(refreshToken);
|
||||
setIsLoggedIn(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update tokens
|
||||
*/
|
||||
const updateTokens = async (accessToken: string, refreshToken: string) : Promise<void> => {
|
||||
accessTokenRef.current = accessToken; // Immediate update
|
||||
refreshTokenRef.current = refreshToken; // Immediate update
|
||||
await Promise.all([
|
||||
localStorage.setItem('accessToken', accessToken),
|
||||
localStorage.setItem('refreshToken', refreshToken),
|
||||
]);
|
||||
|
||||
setAccessToken(accessToken);
|
||||
setRefreshToken(refreshToken);
|
||||
};
|
||||
|
||||
/**
|
||||
* Logout
|
||||
*/
|
||||
const logout = async () : Promise<void> => {
|
||||
// Clear vault in background worker.
|
||||
const logout = async () => {
|
||||
await chrome.runtime.sendMessage({ type: 'CLEAR_VAULT' });
|
||||
|
||||
await Promise.all([
|
||||
localStorage.removeItem('username'),
|
||||
localStorage.removeItem('accessToken'),
|
||||
localStorage.removeItem('refreshToken'),
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
setUsername(null),
|
||||
setAccessToken(null),
|
||||
setRefreshToken(null),
|
||||
setIsLoggedIn(false),
|
||||
]);
|
||||
await chrome.storage.local.remove(['username', 'accessToken', 'refreshToken']);
|
||||
setUsername(null);
|
||||
setIsLoggedIn(false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get access token
|
||||
*/
|
||||
const getAccessToken = () : string | null => accessTokenRef.current || localStorage.getItem('accessToken');
|
||||
|
||||
/**
|
||||
* Get refresh token
|
||||
*/
|
||||
const getRefreshToken = () : string | null => refreshTokenRef.current || localStorage.getItem('refreshToken');
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ isLoggedIn, isInitialized, username, login, updateTokens, logout, getAccessToken, getRefreshToken }}>
|
||||
<AuthContext.Provider value={{ isLoggedIn, isInitialized, username, login, logout }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
|
||||
@@ -5,7 +5,7 @@ type DbContextType = {
|
||||
sqliteClient: SqliteClient | null;
|
||||
dbInitialized: boolean;
|
||||
dbAvailable: boolean;
|
||||
initializeDatabase: (derivedKey: string, vault: string, publicEmailDomains: string[], privateEmailDomains: string[]) => Promise<void>;
|
||||
initializeDatabase: (derivedKey: string, vault: string, publicEmailDomains: string[], privateEmailDomains: string[], vaultRevisionNumber: number) => Promise<void>;
|
||||
clearDatabase: () => void;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
*/
|
||||
const [privateEmailDomains, setPrivateEmailDomains] = useState<string[]>([]);
|
||||
|
||||
const initializeDatabase = useCallback(async (derivedKey: string, vault: string, publicEmailDomains: string[], privateEmailDomains: string[]) => {
|
||||
const initializeDatabase = useCallback(async (derivedKey: string, vault: string, publicEmailDomains: string[], privateEmailDomains: string[], vaultRevisionNumber: number) => {
|
||||
const client = new SqliteClient();
|
||||
await client.initializeFromBase64(vault);
|
||||
setSqliteClient(client);
|
||||
@@ -49,13 +49,16 @@ export const DbProvider: React.FC<{ children: React.ReactNode }> = ({ children }
|
||||
setPublicEmailDomains(publicEmailDomains);
|
||||
setPrivateEmailDomains(privateEmailDomains);
|
||||
|
||||
// Store in background worker
|
||||
// Store in background worker.
|
||||
// TODO: perhaps we can simply pass the full vaultresponse object instead of the individual fields
|
||||
// in case we need to access more fields in the future.
|
||||
chrome.runtime.sendMessage({
|
||||
type: 'STORE_VAULT',
|
||||
derivedKey: derivedKey,
|
||||
vault: vault,
|
||||
publicEmailDomains: publicEmailDomains,
|
||||
privateEmailDomains: privateEmailDomains
|
||||
privateEmailDomains: privateEmailDomains,
|
||||
vaultRevisionNumber: vaultRevisionNumber
|
||||
});
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ const WebApiContext = createContext<WebApiService | null>(null);
|
||||
* WebApiProvider to provide the WebApiService to the app that components can use.
|
||||
*/
|
||||
export const WebApiProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||
const { getAccessToken, getRefreshToken, updateTokens, logout } = useAuth();
|
||||
const { logout } = useAuth();
|
||||
const [webApiService, setWebApiService] = useState<WebApiService | null>(null);
|
||||
|
||||
/**
|
||||
@@ -16,11 +16,6 @@ export const WebApiProvider: React.FC<{ children: React.ReactNode }> = ({ childr
|
||||
*/
|
||||
useEffect(() : void => {
|
||||
const service = new WebApiService(
|
||||
() => getAccessToken(),
|
||||
() => getRefreshToken(),
|
||||
(newAccessToken, newRefreshToken) => {
|
||||
updateTokens(newAccessToken, newRefreshToken);
|
||||
},
|
||||
logout
|
||||
);
|
||||
setWebApiService(service);
|
||||
@@ -40,7 +35,7 @@ export const WebApiProvider: React.FC<{ children: React.ReactNode }> = ({ childr
|
||||
return () : void => {
|
||||
chrome.storage.onChanged.removeListener(handleStorageChange);
|
||||
};
|
||||
}, [getAccessToken, getRefreshToken, updateTokens, logout]);
|
||||
}, [logout]);
|
||||
|
||||
if (!webApiService) {
|
||||
return null;
|
||||
|
||||
@@ -48,7 +48,7 @@ const CredentialsList: React.FC = () => {
|
||||
);
|
||||
|
||||
// Initialize the SQLite context again with the newly retrieved decrypted blob
|
||||
await dbContext.initializeDatabase(passwordHashBase64, decryptedBlob);
|
||||
await dbContext.initializeDatabase(passwordHashBase64, decryptedBlob, vaultResponseJson.vault.publicEmailDomainList, vaultResponseJson.vault.privateEmailDomainList, vaultResponseJson.vault.currentRevisionNumber);
|
||||
} catch (err) {
|
||||
console.error('Refresh error:', err);
|
||||
} finally {
|
||||
|
||||
@@ -72,7 +72,7 @@ const Login: React.FC = () => {
|
||||
const decryptedBlob = await EncryptionUtility.symmetricDecrypt(vaultResponseJson.vault.blob, passwordHashBase64);
|
||||
|
||||
// Initialize the SQLite context with decrypted data
|
||||
await dbContext.initializeDatabase(passwordHashBase64, decryptedBlob, vaultResponseJson.vault.publicEmailDomainList, vaultResponseJson.vault.privateEmailDomainList);
|
||||
await dbContext.initializeDatabase(passwordHashBase64, decryptedBlob, vaultResponseJson.vault.publicEmailDomainList, vaultResponseJson.vault.privateEmailDomainList, vaultResponseJson.vault.currentRevisionNumber);
|
||||
|
||||
// 3. Handle 2FA if required
|
||||
/*
|
||||
|
||||
@@ -53,7 +53,7 @@ const Unlock: React.FC = () => {
|
||||
);
|
||||
|
||||
// Initialize the SQLite context with decrypted data
|
||||
await dbContext.initializeDatabase(passwordHashBase64, decryptedBlob, vaultResponseJson.vault.publicEmailDomainList, vaultResponseJson.vault.privateEmailDomainList);
|
||||
await dbContext.initializeDatabase(passwordHashBase64, decryptedBlob, vaultResponseJson.vault.publicEmailDomainList, vaultResponseJson.vault.privateEmailDomainList, vaultResponseJson.vault.currentRevisionNumber);
|
||||
} catch (err) {
|
||||
setError('Failed to unlock vault. Please check your password and try again.');
|
||||
console.error('Unlock error:', err);
|
||||
@@ -69,10 +69,20 @@ const Unlock: React.FC = () => {
|
||||
showLoading();
|
||||
try {
|
||||
await webApi.logout();
|
||||
} catch (err) {
|
||||
console.error('Logout error:', err);
|
||||
}
|
||||
|
||||
try {
|
||||
await authContext.logout();
|
||||
} finally {
|
||||
} catch (err) {
|
||||
console.error('Logout error:', err);
|
||||
}
|
||||
finally {
|
||||
hideLoading();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
13
browser-extensions/chrome/src/types/webapi/Vault.ts
Normal file
13
browser-extensions/chrome/src/types/webapi/Vault.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export type Vault = {
|
||||
blob: string;
|
||||
createdAt: string;
|
||||
credentialsCount: number;
|
||||
currentRevisionNumber: number;
|
||||
emailAddressList: string[];
|
||||
privateEmailDomainList: string[];
|
||||
publicEmailDomainList: string[];
|
||||
encryptionPublicKey: string;
|
||||
updatedAt: string;
|
||||
username: string;
|
||||
version: string;
|
||||
}
|
||||
@@ -1,18 +1,6 @@
|
||||
export type Vault = {
|
||||
blob: string;
|
||||
createdAt: string;
|
||||
credentialsCount: number;
|
||||
currentRevisionNumber: number;
|
||||
emailAddressList: string[];
|
||||
privateEmailDomainList: string[];
|
||||
publicEmailDomainList: string[];
|
||||
encryptionPublicKey: string;
|
||||
updatedAt: string;
|
||||
username: string;
|
||||
version: string;
|
||||
}
|
||||
import { Vault } from "./Vault";
|
||||
|
||||
export type VaultResponse = {
|
||||
status: number;
|
||||
vault: Vault;
|
||||
}
|
||||
status: number;
|
||||
vault: Vault;
|
||||
}
|
||||
@@ -214,21 +214,25 @@ class SqliteClient {
|
||||
const serviceQuery = `
|
||||
INSERT INTO Services (Id, Name, Url, Logo, CreatedAt, UpdatedAt)
|
||||
VALUES (?, ?, ?, ?, ?, ?)`;
|
||||
const serviceId = crypto.randomUUID();
|
||||
const serviceId = crypto.randomUUID().toUpperCase();
|
||||
const currentDateTime = new Date().toISOString()
|
||||
.replace('T', ' ')
|
||||
.replace('Z', '')
|
||||
.substring(0, 23);
|
||||
const serviceResult = this.executeUpdate(serviceQuery, [
|
||||
serviceId,
|
||||
credential.ServiceName,
|
||||
credential.ServiceUrl ?? null,
|
||||
logoData,
|
||||
new Date().toISOString(),
|
||||
new Date().toISOString()
|
||||
currentDateTime,
|
||||
currentDateTime
|
||||
]);
|
||||
|
||||
// 2. Insert Alias
|
||||
const aliasQuery = `
|
||||
INSERT INTO Aliases (Id, FirstName, LastName, NickName, BirthDate, Gender, Email, CreatedAt, UpdatedAt)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`;
|
||||
const aliasId = crypto.randomUUID();
|
||||
const aliasId = crypto.randomUUID().toUpperCase();
|
||||
const aliasResult = this.executeUpdate(aliasQuery, [
|
||||
aliasId,
|
||||
credential.Alias.FirstName ?? null,
|
||||
@@ -237,23 +241,23 @@ class SqliteClient {
|
||||
credential.Alias.BirthDate ?? null,
|
||||
credential.Alias.Gender ?? null,
|
||||
credential.Alias.Email ?? null,
|
||||
new Date().toISOString(),
|
||||
new Date().toISOString()
|
||||
currentDateTime,
|
||||
currentDateTime
|
||||
]);
|
||||
|
||||
// 3. Insert Credential
|
||||
const credentialQuery = `
|
||||
INSERT INTO Credentials (Id, Username, Notes, ServiceId, AliasId, CreatedAt, UpdatedAt)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)`;
|
||||
const credentialId = crypto.randomUUID();
|
||||
const credentialId = crypto.randomUUID().toUpperCase();
|
||||
const credentialResult = this.executeUpdate(credentialQuery, [
|
||||
credentialId,
|
||||
credential.Username,
|
||||
credential.Notes ?? null,
|
||||
serviceId,
|
||||
aliasId,
|
||||
new Date().toISOString(),
|
||||
new Date().toISOString()
|
||||
currentDateTime,
|
||||
currentDateTime
|
||||
]);
|
||||
|
||||
// 4. Insert Password
|
||||
@@ -261,13 +265,13 @@ class SqliteClient {
|
||||
const passwordQuery = `
|
||||
INSERT INTO Passwords (Id, Value, CredentialId, CreatedAt, UpdatedAt)
|
||||
VALUES (?, ?, ?, ?, ?)`;
|
||||
const passwordId = crypto.randomUUID();
|
||||
const passwordId = crypto.randomUUID().toUpperCase();
|
||||
this.executeUpdate(passwordQuery, [
|
||||
passwordId,
|
||||
credential.Password,
|
||||
credentialId,
|
||||
new Date().toISOString(),
|
||||
new Date().toISOString()
|
||||
currentDateTime,
|
||||
currentDateTime
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,15 +17,9 @@ export class WebApiService {
|
||||
/**
|
||||
* Constructor for the WebApiService class.
|
||||
*
|
||||
* @param {Function} getAccessToken - Function to get the access token.
|
||||
* @param {Function} getRefreshToken - Function to get the refresh token.
|
||||
* @param {Function} updateTokens - Function to update the access and refresh tokens.
|
||||
* @param {Function} handleLogout - Function to handle logout.
|
||||
*/
|
||||
public constructor(
|
||||
private getAccessToken: () => string | null,
|
||||
private getRefreshToken: () => string | null,
|
||||
private updateTokens: (accessToken: string, refreshToken: string) => void,
|
||||
private handleLogout: () => void
|
||||
) {
|
||||
// Load the API URL from storage when service is initialized
|
||||
@@ -35,7 +29,7 @@ export class WebApiService {
|
||||
/**
|
||||
* Initialize the base URL for the API from settings.
|
||||
*/
|
||||
private async initializeBaseUrl() : Promise<void> {
|
||||
public async initializeBaseUrl() : Promise<void> {
|
||||
const result = await chrome.storage.local.get(['apiUrl']);
|
||||
// Trim trailing slash if present
|
||||
this.baseUrl = (result.apiUrl || 'https://app.aliasvault.net/api').replace(/\/$/, '') + '/v1/';
|
||||
@@ -53,7 +47,7 @@ export class WebApiService {
|
||||
const headers = new Headers(options.headers || {});
|
||||
|
||||
// Add authorization header if we have an access token
|
||||
const accessToken = this.getAccessToken();
|
||||
const accessToken = await this.getAccessToken();
|
||||
if (accessToken) {
|
||||
headers.set('Authorization', `Bearer ${accessToken}`);
|
||||
}
|
||||
@@ -101,7 +95,7 @@ export class WebApiService {
|
||||
* Refresh the access token.
|
||||
*/
|
||||
private async refreshAccessToken(): Promise<string | null> {
|
||||
const refreshToken = this.getRefreshToken();
|
||||
const refreshToken = await this.getRefreshToken();
|
||||
if (!refreshToken) {
|
||||
return null;
|
||||
}
|
||||
@@ -114,7 +108,7 @@ export class WebApiService {
|
||||
'X-Ignore-Failure': 'true',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token: this.getAccessToken(),
|
||||
token: await this.getAccessToken(),
|
||||
refreshToken: refreshToken,
|
||||
}),
|
||||
});
|
||||
@@ -180,14 +174,42 @@ export class WebApiService {
|
||||
* Logout and revoke tokens via WebApi.
|
||||
*/
|
||||
public async logout(): Promise<void> {
|
||||
const refreshToken = this.getRefreshToken();
|
||||
const refreshToken = await this.getRefreshToken();
|
||||
if (!refreshToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.post('Auth/revoke', {
|
||||
token: this.getAccessToken(),
|
||||
token: await this.getAccessToken(),
|
||||
refreshToken: refreshToken,
|
||||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current access token from storage.
|
||||
*/
|
||||
private async getAccessToken(): Promise<string | null> {
|
||||
const token = await chrome.storage.local.get('accessToken');
|
||||
console.log('accessToken get', token);
|
||||
return token.accessToken || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current refresh token from storage.
|
||||
*/
|
||||
private async getRefreshToken(): Promise<string | null> {
|
||||
const token = await chrome.storage.local.get('refreshToken');
|
||||
console.log('refreshToken get', token);
|
||||
return token.refreshToken || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update both access and refresh tokens in storage.
|
||||
*/
|
||||
private async updateTokens(accessToken: string, refreshToken: string): Promise<void> {
|
||||
await chrome.storage.local.set({
|
||||
accessToken,
|
||||
refreshToken
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ else
|
||||
{
|
||||
// Error loading alias.
|
||||
GlobalNotificationService.AddErrorMessage("This credentials entry does not exist (anymore). Please try again.");
|
||||
NavigationManager.NavigateTo("/", false, true);
|
||||
NavigationManager.NavigateTo("/credentials", false, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user