diff options
| author | Fuwn <[email protected]> | 2026-06-02 12:59:04 +0000 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-06-02 12:59:04 +0000 |
| commit | 76d710493e2496490f9e2f9894cf581757f4d92e (patch) | |
| tree | dd7b196588d26dd1134322c526ec43eb9721de61 /src/lib | |
| parent | feat(security): add AES-GCM feed-token helper (M5) (diff) | |
| download | due.moe-76d710493e2496490f9e2f9894cf581757f4d92e.tar.xz due.moe-76d710493e2496490f9e2f9894cf581757f4d92e.zip | |
fix(security): replace RSS feed URL tokens with encrypted token (M5)
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/Settings/Categories/RSSFeeds.svelte | 6 | ||||
| -rw-r--r-- | src/lib/Utility/anilistOauth.ts | 28 |
2 files changed, 32 insertions, 2 deletions
diff --git a/src/lib/Settings/Categories/RSSFeeds.svelte b/src/lib/Settings/Categories/RSSFeeds.svelte index 49a6eb5a..303db699 100644 --- a/src/lib/Settings/Categories/RSSFeeds.svelte +++ b/src/lib/Settings/Categories/RSSFeeds.svelte @@ -7,19 +7,21 @@ import { appOrigin } from "$lib/Utility/appOrigin"; import locale from "$stores/locale"; import SettingHint from "../SettingHint.svelte"; -export let user: { accessToken: string; refreshToken: string }; +export let feedToken: string | undefined; </script> <button data-umami-event="Copy RSS Feed URL" onclick={() => { + if (!feedToken) return; + addNotification( options({ heading: get(locale)().notifications?.rssCopied ?? 'RSS feed URL copied to clipboard' }) ); navigator.clipboard.writeText( - `${appOrigin()}/feeds/activity-notifications?token=${user.accessToken}&refresh=${user.refreshToken}` + `${appOrigin()}/feeds/activity-notifications?feed=${feedToken}` ); }} > diff --git a/src/lib/Utility/anilistOauth.ts b/src/lib/Utility/anilistOauth.ts new file mode 100644 index 00000000..26654ec9 --- /dev/null +++ b/src/lib/Utility/anilistOauth.ts @@ -0,0 +1,28 @@ +import { env } from "$env/dynamic/private"; +import { env as publicEnv } from "$env/dynamic/public"; + +// Exchange a refresh token for a fresh access token WITHOUT touching the auth +// cookie. Used by the RSS feed, which is polled by an unattended reader that has +// no session; the interactive /api/oauth/refresh endpoint additionally re-sets +// the cookie, which this deliberately does not. +export const refreshAniListToken = async ( + refreshToken: string, +): Promise<string | null> => { + const formData = new FormData(); + + formData.append("grant_type", "refresh_token"); + formData.append("client_id", publicEnv.PUBLIC_ANILIST_CLIENT_ID as string); + formData.append("client_secret", env.ANILIST_CLIENT_SECRET as string); + formData.append("refresh_token", refreshToken); + + const response = await fetch("https://anilist.co/api/v2/oauth/token", { + method: "POST", + body: formData, + }); + + if (!response.ok) return null; + + const payload = (await response.json()) as { access_token?: string }; + + return payload.access_token ?? null; +}; |