2021-08-22 12:34:58 -07:00
|
|
|
/**
|
|
|
|
* External Auth: workflow for logging in to remote servers.
|
2024-08-28 04:41:08 -07:00
|
|
|
* @module pl-fe/actions/external_auth
|
|
|
|
* @see module:pl-fe/actions/auth
|
|
|
|
* @see module:pl-fe/actions/apps
|
|
|
|
* @see module:pl-fe/actions/oauth
|
2021-08-22 12:34:58 -07:00
|
|
|
*/
|
|
|
|
|
2024-08-06 10:52:36 -07:00
|
|
|
import { instanceSchema, PlApiClient, type Instance } from 'pl-api';
|
2024-08-04 07:09:52 -07:00
|
|
|
|
2024-08-28 04:41:08 -07:00
|
|
|
import { createApp } from 'pl-fe/actions/apps';
|
|
|
|
import { authLoggedIn, verifyCredentials, switchAccount } from 'pl-fe/actions/auth';
|
|
|
|
import { obtainOAuthToken } from 'pl-fe/actions/oauth';
|
|
|
|
import { parseBaseURL } from 'pl-fe/utils/auth';
|
|
|
|
import sourceCode from 'pl-fe/utils/code';
|
|
|
|
import { getQuirks } from 'pl-fe/utils/quirks';
|
|
|
|
import { getInstanceScopes } from 'pl-fe/utils/scopes';
|
2022-01-10 14:25:06 -08:00
|
|
|
|
2024-08-28 04:41:08 -07:00
|
|
|
import type { AppDispatch } from 'pl-fe/store';
|
2022-06-10 10:56:22 -07:00
|
|
|
|
2024-08-04 07:09:52 -07:00
|
|
|
const fetchExternalInstance = (baseURL: string) =>
|
2024-08-19 12:51:43 -07:00
|
|
|
(new PlApiClient(baseURL)).instance.getInstance()
|
2024-08-04 07:09:52 -07:00
|
|
|
.then(instance => instance)
|
2021-08-31 09:02:43 -07:00
|
|
|
.catch(error => {
|
2022-02-18 18:04:03 -08:00
|
|
|
if (error.response?.status === 401) {
|
2021-08-31 09:02:43 -07:00
|
|
|
// Authenticated fetch is enabled.
|
|
|
|
// Continue with a limited featureset.
|
2023-09-23 18:41:24 -07:00
|
|
|
return instanceSchema.parse({});
|
2021-08-31 09:02:43 -07:00
|
|
|
} else {
|
|
|
|
throw error;
|
|
|
|
}
|
|
|
|
});
|
2021-08-22 12:34:58 -07:00
|
|
|
|
2022-06-10 10:56:22 -07:00
|
|
|
const createExternalApp = (instance: Instance, baseURL?: string) =>
|
2024-08-11 01:48:58 -07:00
|
|
|
(dispatch: AppDispatch) => {
|
2022-02-10 17:34:23 -08:00
|
|
|
// Mitra: skip creating the auth app
|
|
|
|
if (getQuirks(instance).noApps) return new Promise(f => f({}));
|
|
|
|
|
2022-02-10 14:33:28 -08:00
|
|
|
const params = {
|
2024-08-29 15:22:14 -07:00
|
|
|
client_name: `${sourceCode.displayName} (${new URL(window.origin).host})`,
|
2022-04-19 12:37:48 -07:00
|
|
|
redirect_uris: `${window.location.origin}/login/external`,
|
2023-01-13 16:42:41 -08:00
|
|
|
website: sourceCode.homepage,
|
2023-01-25 14:32:59 -08:00
|
|
|
scopes: getInstanceScopes(instance),
|
2022-02-10 14:33:28 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
return dispatch(createApp(params, baseURL));
|
|
|
|
};
|
|
|
|
|
2022-06-10 10:56:22 -07:00
|
|
|
const externalAuthorize = (instance: Instance, baseURL: string) =>
|
2024-08-11 01:48:58 -07:00
|
|
|
(dispatch: AppDispatch) => {
|
2023-01-25 14:32:59 -08:00
|
|
|
const scopes = getInstanceScopes(instance);
|
2022-02-10 14:33:28 -08:00
|
|
|
|
2022-06-10 10:56:22 -07:00
|
|
|
return dispatch(createExternalApp(instance, baseURL)).then((app) => {
|
|
|
|
const { client_id, redirect_uri } = app as Record<string, string>;
|
2022-02-10 14:33:28 -08:00
|
|
|
|
|
|
|
const query = new URLSearchParams({
|
|
|
|
client_id,
|
|
|
|
redirect_uri,
|
|
|
|
response_type: 'code',
|
|
|
|
scope: scopes,
|
|
|
|
});
|
|
|
|
|
2024-05-11 14:37:37 -07:00
|
|
|
localStorage.setItem('plfe:external:app', JSON.stringify(app));
|
|
|
|
localStorage.setItem('plfe:external:baseurl', baseURL);
|
|
|
|
localStorage.setItem('plfe:external:scopes', scopes);
|
2022-02-10 14:33:28 -08:00
|
|
|
|
|
|
|
window.location.href = `${baseURL}/oauth/authorize?${query.toString()}`;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2024-05-12 16:18:04 -07:00
|
|
|
const externalLogin = (host: string) =>
|
2022-06-10 10:56:22 -07:00
|
|
|
(dispatch: AppDispatch) => {
|
2021-08-22 12:34:58 -07:00
|
|
|
const baseURL = parseBaseURL(host) || parseBaseURL(`https://${host}`);
|
|
|
|
|
2022-06-10 10:56:22 -07:00
|
|
|
return fetchExternalInstance(baseURL).then((instance) => {
|
2023-09-20 14:09:22 -07:00
|
|
|
dispatch(externalAuthorize(instance, baseURL));
|
2021-08-22 12:34:58 -07:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2024-05-12 16:18:04 -07:00
|
|
|
const loginWithCode = (code: string) =>
|
2022-06-10 10:56:22 -07:00
|
|
|
(dispatch: AppDispatch) => {
|
2024-05-11 14:37:37 -07:00
|
|
|
const { client_id, client_secret, redirect_uri } = JSON.parse(localStorage.getItem('plfe:external:app')!);
|
|
|
|
const baseURL = localStorage.getItem('plfe:external:baseurl')!;
|
2024-08-11 01:48:58 -07:00
|
|
|
const scope = localStorage.getItem('plfe:external:scopes')!;
|
2021-08-22 12:34:58 -07:00
|
|
|
|
2024-08-06 10:52:36 -07:00
|
|
|
const params = {
|
2021-08-22 12:34:58 -07:00
|
|
|
client_id,
|
|
|
|
client_secret,
|
2021-08-22 12:46:40 -07:00
|
|
|
redirect_uri,
|
2021-08-22 12:34:58 -07:00
|
|
|
grant_type: 'authorization_code',
|
2021-08-22 15:13:01 -07:00
|
|
|
scope,
|
2021-08-22 12:34:58 -07:00
|
|
|
code,
|
|
|
|
};
|
|
|
|
|
|
|
|
return dispatch(obtainOAuthToken(params, baseURL))
|
2024-08-06 10:52:36 -07:00
|
|
|
.then((token) => dispatch(authLoggedIn(token)))
|
|
|
|
.then(({ access_token }) => dispatch(verifyCredentials(access_token as string, baseURL)))
|
|
|
|
.then((account) => dispatch(switchAccount(account.id)))
|
2021-08-22 12:34:58 -07:00
|
|
|
.then(() => window.location.href = '/');
|
|
|
|
};
|
2024-05-12 16:18:04 -07:00
|
|
|
|
|
|
|
export {
|
|
|
|
externalLogin,
|
|
|
|
loginWithCode,
|
|
|
|
};
|