141 lines
4.6 KiB
JavaScript
141 lines
4.6 KiB
JavaScript
/**
|
|
* External Auth: workflow for logging in to remote servers.
|
|
* @module soapbox/actions/external_auth
|
|
* @see module:soapbox/actions/auth
|
|
* @see module:soapbox/actions/apps
|
|
* @see module:soapbox/actions/oauth
|
|
*/
|
|
|
|
import { createApp } from 'soapbox/actions/apps';
|
|
import { authLoggedIn, verifyCredentials, switchAccount } from 'soapbox/actions/auth';
|
|
import { obtainOAuthToken } from 'soapbox/actions/oauth';
|
|
import { normalizeInstance } from 'soapbox/normalizers';
|
|
import { parseBaseURL } from 'soapbox/utils/auth';
|
|
import sourceCode from 'soapbox/utils/code';
|
|
import { getWalletAndSign } from 'soapbox/utils/ethereum';
|
|
import { getFeatures } from 'soapbox/utils/features';
|
|
import { getQuirks } from 'soapbox/utils/quirks';
|
|
|
|
import { baseClient } from '../api';
|
|
|
|
const fetchExternalInstance = baseURL => {
|
|
return baseClient(null, baseURL)
|
|
.get('/api/v1/instance')
|
|
.then(({ data: instance }) => normalizeInstance(instance))
|
|
.catch(error => {
|
|
if (error.response?.status === 401) {
|
|
// Authenticated fetch is enabled.
|
|
// Continue with a limited featureset.
|
|
return normalizeInstance({});
|
|
} else {
|
|
throw error;
|
|
}
|
|
});
|
|
};
|
|
|
|
function createExternalApp(instance, baseURL) {
|
|
return (dispatch, getState) => {
|
|
// Mitra: skip creating the auth app
|
|
if (getQuirks(instance).noApps) return new Promise(f => f({}));
|
|
|
|
const { scopes } = getFeatures(instance);
|
|
|
|
const params = {
|
|
client_name: sourceCode.displayName,
|
|
redirect_uris: `${window.location.origin}/auth/external`,
|
|
website: sourceCode.homepage,
|
|
scopes,
|
|
};
|
|
|
|
return dispatch(createApp(params, baseURL));
|
|
};
|
|
}
|
|
|
|
function externalAuthorize(instance, baseURL) {
|
|
return (dispatch, getState) => {
|
|
const { scopes } = getFeatures(instance);
|
|
|
|
return dispatch(createExternalApp(instance, baseURL)).then(app => {
|
|
const { client_id, redirect_uri } = app;
|
|
|
|
const query = new URLSearchParams({
|
|
client_id,
|
|
redirect_uri,
|
|
response_type: 'code',
|
|
scope: scopes,
|
|
});
|
|
|
|
localStorage.setItem('soapbox:external:app', JSON.stringify(app));
|
|
localStorage.setItem('soapbox:external:baseurl', baseURL);
|
|
localStorage.setItem('soapbox:external:scopes', scopes);
|
|
|
|
window.location.href = `${baseURL}/oauth/authorize?${query.toString()}`;
|
|
});
|
|
};
|
|
}
|
|
|
|
export function externalEthereumLogin(instance, baseURL) {
|
|
return (dispatch, getState) => {
|
|
const loginMessage = instance.get('login_message');
|
|
|
|
return getWalletAndSign(loginMessage).then(({ wallet, signature }) => {
|
|
return dispatch(createExternalApp(instance, baseURL)).then(app => {
|
|
const params = {
|
|
grant_type: 'ethereum',
|
|
wallet_address: wallet.toLowerCase(),
|
|
client_id: app.client_id,
|
|
client_secret: app.client_secret,
|
|
password: signature,
|
|
redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
|
|
scope: getFeatures(instance).scopes,
|
|
};
|
|
|
|
return dispatch(obtainOAuthToken(params, baseURL))
|
|
.then(token => dispatch(authLoggedIn(token)))
|
|
.then(({ access_token }) => dispatch(verifyCredentials(access_token, baseURL)))
|
|
.then(account => dispatch(switchAccount(account.id)))
|
|
.then(() => window.location.href = '/');
|
|
});
|
|
});
|
|
};
|
|
}
|
|
|
|
export function externalLogin(host) {
|
|
return (dispatch, getState) => {
|
|
const baseURL = parseBaseURL(host) || parseBaseURL(`https://${host}`);
|
|
|
|
return fetchExternalInstance(baseURL).then(instance => {
|
|
const features = getFeatures(instance);
|
|
const quirks = getQuirks(instance);
|
|
|
|
if (features.ethereumLogin && quirks.noOAuthForm) {
|
|
return dispatch(externalEthereumLogin(instance, baseURL));
|
|
} else {
|
|
return dispatch(externalAuthorize(instance, baseURL));
|
|
}
|
|
});
|
|
};
|
|
}
|
|
|
|
export function loginWithCode(code) {
|
|
return (dispatch, getState) => {
|
|
const { client_id, client_secret, redirect_uri } = JSON.parse(localStorage.getItem('soapbox:external:app'));
|
|
const baseURL = localStorage.getItem('soapbox:external:baseurl');
|
|
const scope = localStorage.getItem('soapbox:external:scopes');
|
|
|
|
const params = {
|
|
client_id,
|
|
client_secret,
|
|
redirect_uri,
|
|
grant_type: 'authorization_code',
|
|
scope,
|
|
code,
|
|
};
|
|
|
|
return dispatch(obtainOAuthToken(params, baseURL))
|
|
.then(token => dispatch(authLoggedIn(token)))
|
|
.then(({ access_token }) => dispatch(verifyCredentials(access_token, baseURL)))
|
|
.then(account => dispatch(switchAccount(account.id)))
|
|
.then(() => window.location.href = '/');
|
|
};
|
|
}
|