diff --git a/packages/pl-api/lib/client.ts b/packages/pl-api/lib/client.ts index 1b1e675f5..30a544602 100644 --- a/packages/pl-api/lib/client.ts +++ b/packages/pl-api/lib/client.ts @@ -1260,11 +1260,11 @@ class PlApiClient { * Requires features{@link Features['sessions']}. * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#get-apioauth_tokens} */ - getOauthTokens: async () => { - const response = await this.request('/api/oauth_tokens'); - - return v.parse(filteredArray(oauthTokenSchema), response.json); - }, + getOauthTokens: () => this.#paginatedGet( + this.features.version.software === GOTOSOCIAL ? '/api/v1/tokens' : '/api/oauth_tokens', + {}, + oauthTokenSchema, + ), /** * Revoke a user session by its ID @@ -1272,8 +1272,17 @@ class PlApiClient { * Requires features{@link Features['sessions']}. * @see {@link https://docs.pleroma.social/backend/development/API/pleroma_api/#delete-apioauth_tokensid} */ - deleteOauthToken: async (oauthTokenId: number) => { - const response = await this.request(`/api/oauth_tokens/${oauthTokenId}`, { method: 'DELETE' }); + deleteOauthToken: async (oauthTokenId: string) => { + let response; + + switch (this.features.version.software) { + case GOTOSOCIAL: + response = await this.request(`/api/v1/tokens/${oauthTokenId}/invalidate`, { method: 'POST' }); + break; + default: + response = await this.request(`/api/oauth_tokens/${oauthTokenId}`, { method: 'DELETE' }); + break; + } return response.json as {}; }, diff --git a/packages/pl-api/lib/entities/oauth-token.ts b/packages/pl-api/lib/entities/oauth-token.ts index 808136fc5..ea1488d81 100644 --- a/packages/pl-api/lib/entities/oauth-token.ts +++ b/packages/pl-api/lib/entities/oauth-token.ts @@ -8,14 +8,29 @@ import { datetimeSchema } from './utils'; */ const oauthTokenSchema = v.pipe( v.any(), - v.transform((token: any) => ({ - ...token, - valid_until: token?.valid_until?.padEnd(27, 'Z'), - })), + v.transform((token: any) => { + if (token.application) { + return { + ...token, + app_name: token.application.name, + app_website: token.application.website, + scopes: token.scope.split(' '), + }; + } + + return { + ...token, + valid_until: token?.valid_until?.padEnd(27, 'Z'), + }; + }), v.object({ app_name: v.string(), - id: v.number(), - valid_until: datetimeSchema, + app_website: v.fallback(v.string(), ''), + id: v.pipe(v.unknown(), v.transform(String)), + created_at: v.fallback(v.nullable(datetimeSchema), null), + valid_until: v.fallback(v.nullable(datetimeSchema), null), + last_used: v.fallback(v.nullable(datetimeSchema), null), + scopes: v.fallback(v.array(v.string()), []), }), ); diff --git a/packages/pl-api/lib/features.ts b/packages/pl-api/lib/features.ts index 3684abaab..5b1b169d4 100644 --- a/packages/pl-api/lib/features.ts +++ b/packages/pl-api/lib/features.ts @@ -1267,8 +1267,14 @@ const getFeatures = (instance: Instance) => { * Ability to manage account sessions. * @see GET /api/oauth_tokens.json * @see DELETE /api/oauth_tokens/:id + * @see GET /api/v1/tokens + * @see GET /api/v1/tokens/:id + * @see POST /api/v1/tokens/:id/invalidate */ - sessions: v.software === PLEROMA, + sessions: any([ + v.software === PLEROMA, + v.software === GOTOSOCIAL && gte(v.version, '0.18.2'), + ]), /** * Can store client settings in the database. diff --git a/packages/pl-api/package.json b/packages/pl-api/package.json index f7a5eaa5c..2269af588 100644 --- a/packages/pl-api/package.json +++ b/packages/pl-api/package.json @@ -1,6 +1,6 @@ { "name": "pl-api", - "version": "1.0.0-rc.29", + "version": "1.0.0-rc.30", "type": "module", "homepage": "https://github.com/mkljczk/pl-fe/tree/develop/packages/pl-api", "repository": { diff --git a/packages/pl-fe/package.json b/packages/pl-fe/package.json index a2766bbbd..4a5c7b9c1 100644 --- a/packages/pl-fe/package.json +++ b/packages/pl-fe/package.json @@ -103,7 +103,7 @@ "multiselect-react-dropdown": "^2.0.25", "mutative": "^1.1.0", "path-browserify": "^1.0.1", - "pl-api": "^1.0.0-rc.29", + "pl-api": "^1.0.0-rc.30", "postcss": "^8.4.49", "process": "^0.11.10", "punycode": "^2.1.1", diff --git a/packages/pl-fe/src/actions/security.ts b/packages/pl-fe/src/actions/security.ts index 16f17e493..4b48cdb58 100644 --- a/packages/pl-fe/src/actions/security.ts +++ b/packages/pl-fe/src/actions/security.ts @@ -21,11 +21,11 @@ const REVOKE_TOKEN_SUCCESS = 'REVOKE_TOKEN_SUCCESS' as const; const fetchOAuthTokens = () => (dispatch: AppDispatch, getState: () => RootState) => - getClient(getState).settings.getOauthTokens().then((tokens) => { + getClient(getState).settings.getOauthTokens().then(({ items: tokens }) => { dispatch({ type: FETCH_TOKENS_SUCCESS, tokens }); }); -const revokeOAuthTokenById = (tokenId: number) => +const revokeOAuthTokenById = (tokenId: string) => (dispatch: AppDispatch, getState: () => RootState) => getClient(getState).settings.deleteOauthToken(tokenId).then(() => { dispatch({ type: REVOKE_TOKEN_SUCCESS, tokenId }); @@ -65,7 +65,7 @@ const moveAccount = (targetAccount: string, password: string) => type SecurityAction = | { type: typeof FETCH_TOKENS_SUCCESS; tokens: Array } - | { type: typeof REVOKE_TOKEN_SUCCESS; tokenId: number } + | { type: typeof REVOKE_TOKEN_SUCCESS; tokenId: string } | { type: typeof AUTH_LOGGED_OUT; account: Account } export { diff --git a/packages/pl-fe/src/components/badge.tsx b/packages/pl-fe/src/components/badge.tsx index f24cc90e2..92ccf23b3 100644 --- a/packages/pl-fe/src/components/badge.tsx +++ b/packages/pl-fe/src/components/badge.tsx @@ -4,7 +4,7 @@ import React, { useMemo } from 'react'; import { hexToHsl } from 'pl-fe/utils/theme'; interface IBadge { - title: React.ReactNode; + title: React.ReactNode | string; slug: string; color?: string; } diff --git a/packages/pl-fe/src/features/auth-token-list/index.tsx b/packages/pl-fe/src/features/auth-token-list/index.tsx index fd072ac64..59eac94cf 100644 --- a/packages/pl-fe/src/features/auth-token-list/index.tsx +++ b/packages/pl-fe/src/features/auth-token-list/index.tsx @@ -1,11 +1,13 @@ import React, { useEffect } from 'react'; -import { defineMessages, FormattedDate, useIntl } from 'react-intl'; +import { defineMessages, FormattedDate, FormattedMessage, useIntl } from 'react-intl'; import { fetchOAuthTokens, revokeOAuthTokenById } from 'pl-fe/actions/security'; +import Badge from 'pl-fe/components/badge'; import Button from 'pl-fe/components/ui/button'; import Card, { CardBody, CardHeader, CardTitle } from 'pl-fe/components/ui/card'; import Column from 'pl-fe/components/ui/column'; import HStack from 'pl-fe/components/ui/hstack'; +import Icon from 'pl-fe/components/ui/icon'; import Spinner from 'pl-fe/components/ui/spinner'; import Stack from 'pl-fe/components/ui/stack'; import Text from 'pl-fe/components/ui/text'; @@ -51,19 +53,82 @@ const AuthToken: React.FC = ({ token, isCurrent }) => { return (
- - - {token.app_name} + + + + + {token.app_name} + {token.app_website && ( + + + + )} + + + {token.scopes?.length > 0 && ( + + + + + {token.scopes.map((scope, index) => ( + + ))} + + )} + {token.created_at && ( + + }} + /> + + )} + {token.last_used && ( + + }} + /> + + )} {token.valid_until && ( - }} /> )} diff --git a/packages/pl-fe/src/reducers/security.ts b/packages/pl-fe/src/reducers/security.ts index 3c78a5dea..d858b75fe 100644 --- a/packages/pl-fe/src/reducers/security.ts +++ b/packages/pl-fe/src/reducers/security.ts @@ -26,7 +26,7 @@ const initialState: State = { }, }; -const deleteToken = (state: State, tokenId: number) => state.tokens = state.tokens.filter(token => token.id !== tokenId); +const deleteToken = (state: State, tokenId: string) => state.tokens = state.tokens.filter(token => token.id !== tokenId); const importMfa = (state: State, data: any) => state.mfa = data; diff --git a/packages/pl-fe/yarn.lock b/packages/pl-fe/yarn.lock index 2d157c36b..f62bd8966 100644 --- a/packages/pl-fe/yarn.lock +++ b/packages/pl-fe/yarn.lock @@ -7553,10 +7553,10 @@ pkg-dir@^4.1.0: dependencies: find-up "^4.0.0" -pl-api@^1.0.0-rc.29: - version "1.0.0-rc.29" - resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-1.0.0-rc.29.tgz#3d49281b15b1fca70cc631bc3107ca27f68c6034" - integrity sha512-cgQUX14DyaoSpPwU1eBB+VkABCd5YwHqFisJ0erVqYJSbKG17oeyrFiu/BCer7/ExQR8GF0UyoZvcbkCsQv1eQ== +pl-api@^1.0.0-rc.30: + version "1.0.0-rc.30" + resolved "https://registry.yarnpkg.com/pl-api/-/pl-api-1.0.0-rc.30.tgz#1702557d5e723ba40c73323782c4ad3910e3f908" + integrity sha512-9rmx87EV3oqI3e2ZonHVRcYBS3J1SyfQBNMmWwPmr0aPiA/kVeDcCgRjlxc0Z0srLWGg4Bwfxdcp8zx7aSRMqg== dependencies: blurhash "^2.0.5" http-link-header "^1.1.3"