Merge pull request #131 from teaxyz/secure-authorization

#129 secure auth
This commit is contained in:
Neil 2023-01-10 21:01:52 +08:00 committed by GitHub
commit c93ce6ec0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 477 additions and 305 deletions

View file

@ -42,6 +42,8 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@tauri-apps/api": "^1.2.0", "@tauri-apps/api": "^1.2.0",
"@types/bcryptjs": "^2.4.2",
"bcryptjs": "^2.4.3",
"buffer": "^6.0.3", "buffer": "^6.0.3",
"fuse.js": "^6.6.2", "fuse.js": "^6.6.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",

View file

@ -20,20 +20,41 @@ import type { GUIPackage, Course, Category, AuthStatus } from '../types';
import * as mock from './mock'; import * as mock from './mock';
import { PackageStates } from '../types'; import { PackageStates } from '../types';
import { getSession } from '$libs/stores/auth';
import type { Session } from '$libs/stores/auth';
import bcrypt from 'bcryptjs';
export const apiBaseUrl = 'https://api.tea.xyz/v1'; export const apiBaseUrl = 'https://api.tea.xyz/v1';
// const apiBaseUrl = 'http://localhost:3000/v1'; // export const apiBaseUrl = 'http://localhost:3000/v1';
async function getHeaders(path: string, session: Session) {
const unixMs = new Date().getTime();
const unixHexSecs = Math.round(unixMs / 1000).toString(16); // hex
const deviceId = session.device_id?.split('-')[0];
const preHash = [unixHexSecs, session.key, deviceId, path].join('');
const Authorization = bcrypt.hashSync(preHash, 10);
return {
Authorization,
['tea-ts']: unixMs.toString(),
['tea-uid']: session.user?.developer_id,
['tea-gui_id']: session.device_id
};
}
async function get<T>(path: string, query?: { [key: string]: string }) { async function get<T>(path: string, query?: { [key: string]: string }) {
console.log('path', path); const [session, client] = await Promise.all([getSession(), getClient()]);
const client = await getClient();
const uri = join(apiBaseUrl, path); const uri = join(apiBaseUrl, path);
console.log('uri:', uri);
const headers =
session?.device_id && session?.user
? await getHeaders(`GET/${path}`, session)
: { Authorization: 'public ' };
const { data } = await client.get<T>(uri.toString(), { const { data } = await client.get<T>(uri.toString(), {
headers: { headers,
Authorization: 'public' // TODO: figure out why req w/o Authorization does not work
// 'cache-control': 'no-cache'
},
query: query || {} query: query || {}
}); });
return data; return data;
@ -79,6 +100,7 @@ export async function getPackageReviews(full_name: string): Promise<Review[]> {
const reviews: Review[] = await get<Review[]>( const reviews: Review[] = await get<Review[]>(
`packages/${full_name.replaceAll('/', ':')}/reviews` `packages/${full_name.replaceAll('/', ':')}/reviews`
); );
return reviews; return reviews;
} }

View file

@ -46,7 +46,7 @@ function initPackagesStore() {
export const packagesStore = initPackagesStore(); export const packagesStore = initPackagesStore();
export const initializeFeaturedPackages = async () => { export const initializeFeaturedPackages = async () => {
console.log('initialzie featured packages'); console.log('intialize featured packages');
const packages = await getFeaturedPackages(); const packages = await getFeaturedPackages();
featuredPackages.set(packages); featuredPackages.set(packages);
}; };

View file

@ -5,14 +5,25 @@ import { getDeviceAuth, registerDevice } from '@api';
import type { Developer } from '@tea/ui/types'; import type { Developer } from '@tea/ui/types';
const basePath = '.tea/tea.xyz/gui'; const basePath = '.tea/tea.xyz/gui';
interface Session { export interface Session {
device_id?: string; device_id?: string;
key?: string; key?: string;
user?: Developer; user?: Developer;
} }
export let session: Session | null = null;
export const getSession = async (): Promise<Session | null> => {
if (session && session?.user) return session;
const sessionFilePath = await join(basePath, 'tmp.dat');
const encryptedData = await readTextFile(sessionFilePath, {
dir: BaseDirectory.Home
});
session = JSON.parse(encryptedData || '{}') as Session;
return session;
};
export default function initAuthStore() { export default function initAuthStore() {
const session = writable<Session>({}); const sessionStore = writable<Session>({});
let pollLoop = 0; let pollLoop = 0;
const deviceIdStore = writable<string>(''); const deviceIdStore = writable<string>('');
@ -20,7 +31,8 @@ export default function initAuthStore() {
initSession().then((sess) => { initSession().then((sess) => {
if (sess) { if (sess) {
session.set(sess); session = sess;
sessionStore.set(sess);
deviceIdStore.set(sess.device_id!); deviceIdStore.set(sess.device_id!);
deviceId = sess.device_id!; deviceId = sess.device_id!;
} }
@ -35,7 +47,7 @@ export default function initAuthStore() {
user: data.user user: data.user
}; };
saveLocallySessionData(localSession); saveLocallySessionData(localSession);
session.set(localSession); sessionStore.set(localSession);
} }
async function pollSession() { async function pollSession() {
@ -70,7 +82,7 @@ export default function initAuthStore() {
deviceId, deviceId,
deviceIdStore, deviceIdStore,
subscribe: (cb: (u: Developer) => void) => { subscribe: (cb: (u: Developer) => void) => {
return session.subscribe((v) => v?.user && cb(v.user)); return sessionStore.subscribe((v) => v?.user && cb(v.user));
}, },
pollSession pollSession
}; };
@ -86,14 +98,12 @@ const initSession = async (): Promise<Session | void> => {
}; };
const getLocalSessionData = async (): Promise<Session | void> => { const getLocalSessionData = async (): Promise<Session | void> => {
const sessionFilePath = await join(basePath, 'tmp.dat');
let data: Session; let data: Session;
try { try {
const encryptedData = await readTextFile(sessionFilePath, { const session = await getSession();
dir: BaseDirectory.Home if (session) {
}); data = session;
// TODO: decrypt then return }
data = JSON.parse(encryptedData || '{}');
} catch (error) { } catch (error) {
console.error(error); console.error(error);
const deviceId = await registerDevice(); const deviceId = await registerDevice();

View file

@ -9,7 +9,6 @@
import News from '$components/News/News.svelte'; import News from '$components/News/News.svelte';
import CategorizedPackages from '$components/CategorizedPackages/CategorizedPackages.svelte'; import CategorizedPackages from '$components/CategorizedPackages/CategorizedPackages.svelte';
backLink.set(''); backLink.set('');
console.log('test', window.location);
</script> </script>
<div> <div>

File diff suppressed because it is too large Load diff