aboutsummaryrefslogtreecommitdiff
path: root/extension/src/features/auth-api.ts
blob: e3f6c21a8043007feeee177ce8add044d0f69208 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Copyright (C) 2023 Vaughn Nugent
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

import { AxiosInstance } from "axios";
import { get } from "@vueuse/core";
import { computed } from "vue";
import { usePkiAuth, useSession, useUser } from "@vnuge/vnlib.browser";
import type { ClientStatus } from "./types";
import type { AppSettings } from "./settings";
import type { JsonObject } from "type-fest";
import { type FeatureApi, type BgRuntime, type IFeatureExport, exportForegroundApi, popupAndOptionsOnly, popupOnly } from "./framework";
import { waitForChangeFn } from "./util";

export interface ProectedHandler<T extends JsonObject> {
    (message: T): Promise<any>
}

export interface MessageHandler<T extends JsonObject> {
    (message: T): Promise<any>
}

export interface ApiMessageHandler<T extends JsonObject> {
    (message: T, apiHandle: { axios: AxiosInstance }): Promise<any>
}

export interface UserApi extends FeatureApi {
    login: (token: string) => Promise<boolean>
    logout: () => Promise<void>
    getProfile: () => Promise<any>
    getStatus: () => Promise<ClientStatus>
    waitForChange: () => Promise<void>
}

export const useAuthApi = (): IFeatureExport<AppSettings, UserApi> => {

    return {
        background: ({ state, onInstalled }:BgRuntime<AppSettings>): UserApi =>{
            const { loggedIn, clearLoginState } = useSession();
            const { currentConfig } = state
            const { logout, getProfile, heartbeat, userName } = useUser();
            const currentPkiPath = computed(() => `${currentConfig.value.accountBasePath}/pki`)
            
            //Use pki login controls
            const { login } = usePkiAuth(currentPkiPath as any)

            //We can send post messages to the server heartbeat endpoint to get status
            const runHeartbeat = async () => {
                //Only run if the api thinks its logged in, and config is enabled
                if (!loggedIn.value || currentConfig.value.heartbeat !== true) {
                    return
                }

                try {
                    //Post against the heartbeat endpoint
                    await heartbeat()
                }
                catch (error: any) {
                    if (error.response?.status === 401 || error.response?.status === 403) {
                        //If we get a 401, the user is no longer logged in
                        clearLoginState()
                    }
                }
            }

            //Install hook for interval
            onInstalled(() => {
                //Configure interval to run every 5 minutes to update the status
                setInterval(runHeartbeat, 60 * 1000);
                //Run immediately
                runHeartbeat();
                return Promise.resolve();
            })

            return {
                waitForChange: waitForChangeFn([currentConfig, loggedIn, userName]),
                login: popupOnly(async (token: string): Promise<boolean> => {
                    //Perform login
                    await login(token)
                    //load profile
                    getProfile()
                    return true;
                }),
                logout: popupOnly(async (): Promise<void> => {
                    //Perform logout
                    await logout()
                    //Cleanup after logout
                    clearLoginState()
                }),
                getProfile: popupAndOptionsOnly(getProfile),
                async getStatus (){
                    return {
                        //Logged in if the cookie is set and the api flag is set
                        loggedIn: get(loggedIn),
                        //username
                        userName: get(userName),
                    } as ClientStatus
                },
            }
        },
        foreground: exportForegroundApi<UserApi>([
            'login',
            'logout',
            'getProfile',
            'getStatus',
            'waitForChange'
        ]),
    } 
}