diff --git a/app/soapbox/actions/auth.ts b/app/soapbox/actions/auth.ts index 436c87ff2..a0ef8b447 100644 --- a/app/soapbox/actions/auth.ts +++ b/app/soapbox/actions/auth.ts @@ -20,8 +20,8 @@ import KVStore from 'soapbox/storage/kv-store'; import toast from 'soapbox/toast'; import { getLoggedInAccount, parseBaseURL } from 'soapbox/utils/auth'; import sourceCode from 'soapbox/utils/code'; -import { getFeatures } from 'soapbox/utils/features'; import { normalizeUsername } from 'soapbox/utils/input'; +import { getScopes } from 'soapbox/utils/scopes'; import { isStandalone } from 'soapbox/utils/state'; import api, { baseClient } from '../api'; @@ -55,12 +55,6 @@ export const messages = defineMessages({ const noOp = () => new Promise(f => f(undefined)); -const getScopes = (state: RootState) => { - const instance = state.instance; - const { scopes } = getFeatures(instance); - return scopes; -}; - const createAppAndToken = () => (dispatch: AppDispatch) => dispatch(getAuthApp()).then(() => diff --git a/app/soapbox/actions/consumer-auth.ts b/app/soapbox/actions/consumer-auth.ts index 171941577..72c928dba 100644 --- a/app/soapbox/actions/consumer-auth.ts +++ b/app/soapbox/actions/consumer-auth.ts @@ -3,7 +3,7 @@ import axios from 'axios'; import * as BuildConfig from 'soapbox/build-config'; import { isURL } from 'soapbox/utils/auth'; import sourceCode from 'soapbox/utils/code'; -import { getFeatures } from 'soapbox/utils/features'; +import { getScopes } from 'soapbox/utils/scopes'; import { createApp } from './apps'; @@ -11,8 +11,7 @@ import type { AppDispatch, RootState } from 'soapbox/store'; const createProviderApp = () => { return async(dispatch: AppDispatch, getState: () => RootState) => { - const state = getState(); - const { scopes } = getFeatures(state.instance); + const scopes = getScopes(getState()); const params = { client_name: sourceCode.displayName, @@ -29,8 +28,7 @@ export const prepareRequest = (provider: string) => { return async(dispatch: AppDispatch, getState: () => RootState) => { const baseURL = isURL(BuildConfig.BACKEND_URL) ? BuildConfig.BACKEND_URL : ''; - const state = getState(); - const { scopes } = getFeatures(state.instance); + const scopes = getScopes(getState()); const app = await dispatch(createProviderApp()); const { client_id, redirect_uri } = app; diff --git a/app/soapbox/actions/external-auth.ts b/app/soapbox/actions/external-auth.ts index 064e100c9..bf0618395 100644 --- a/app/soapbox/actions/external-auth.ts +++ b/app/soapbox/actions/external-auth.ts @@ -15,10 +15,11 @@ 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 { getScopes } from 'soapbox/utils/scopes'; import { baseClient } from '../api'; -import type { AppDispatch } from 'soapbox/store'; +import type { AppDispatch, RootState } from 'soapbox/store'; import type { Instance } from 'soapbox/types/entities'; const fetchExternalInstance = (baseURL?: string) => { @@ -37,25 +38,23 @@ const fetchExternalInstance = (baseURL?: string) => { }; const createExternalApp = (instance: Instance, baseURL?: string) => - (dispatch: AppDispatch) => { + (dispatch: AppDispatch, getState: () => RootState) => { // 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, + client_name: sourceCode.displayName, redirect_uris: `${window.location.origin}/login/external`, - website: sourceCode.homepage, - scopes, + website: sourceCode.homepage, + scopes: getScopes(getState()), }; return dispatch(createApp(params, baseURL)); }; const externalAuthorize = (instance: Instance, baseURL: string) => - (dispatch: AppDispatch) => { - const { scopes } = getFeatures(instance); + (dispatch: AppDispatch, getState: () => RootState) => { + const scopes = getScopes(getState()); return dispatch(createExternalApp(instance, baseURL)).then((app) => { const { client_id, redirect_uri } = app as Record; @@ -76,7 +75,7 @@ const externalAuthorize = (instance: Instance, baseURL: string) => }; const externalEthereumLogin = (instance: Instance, baseURL?: string) => - (dispatch: AppDispatch) => { + (dispatch: AppDispatch, getState: () => RootState) => { const loginMessage = instance.login_message; return getWalletAndSign(loginMessage).then(({ wallet, signature }) => { @@ -89,7 +88,7 @@ const externalEthereumLogin = (instance: Instance, baseURL?: string) => client_secret: client_secret, password: signature as string, redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', - scope: getFeatures(instance).scopes, + scope: getScopes(getState()), }; return dispatch(obtainOAuthToken(params, baseURL)) diff --git a/app/soapbox/utils/features.ts b/app/soapbox/utils/features.ts index e59cdfff3..6d1ce4749 100644 --- a/app/soapbox/utils/features.ts +++ b/app/soapbox/utils/features.ts @@ -666,13 +666,6 @@ const getInstanceFeatures = (instance: Instance) => { v.software === PLEROMA, ]), - /** - * List of OAuth scopes supported by both Soapbox and the backend. - * @see POST /api/v1/apps - * @see POST /oauth/token - */ - scopes: v.software === PLEROMA ? 'read write follow push admin' : 'read write follow push', - /** * Ability to search statuses from the given account. * @see {@link https://docs.joinmastodon.org/methods/search/} diff --git a/app/soapbox/utils/scopes.ts b/app/soapbox/utils/scopes.ts new file mode 100644 index 000000000..32c5ef04f --- /dev/null +++ b/app/soapbox/utils/scopes.ts @@ -0,0 +1,23 @@ +import { RootState } from 'soapbox/store'; + +import { PLEROMA, parseVersion } from './features'; + +/** + * Get the OAuth scopes to use for login & signup. + * Mastodon will refuse scopes it doesn't know, so care is needed. + */ +const getScopes = (state: RootState) => { + const instance = state.instance; + const v = parseVersion(instance.version); + + switch (v.software) { + case PLEROMA: + return 'read write follow push admin'; + default: + return 'read write follow push'; + } +}; + +export { + getScopes, +}; \ No newline at end of file