diff --git a/.github/ISSUE_TEMPLATE/2_bug_provider.yml b/.github/ISSUE_TEMPLATE/2_bug_provider.yml index 1ee4409a23..c79e045cd6 100644 --- a/.github/ISSUE_TEMPLATE/2_bug_provider.yml +++ b/.github/ISSUE_TEMPLATE/2_bug_provider.yml @@ -63,6 +63,7 @@ body: - "Frontegg" - "Keycloak" - "Kinde" + - "Last.fm" - "Line" - "LinkedIn" - "Logto" diff --git a/docs/public/img/providers/lastfm.svg b/docs/public/img/providers/lastfm.svg new file mode 100644 index 0000000000..6e1fb9f90a --- /dev/null +++ b/docs/public/img/providers/lastfm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/core/src/providers/lastfm.ts b/packages/core/src/providers/lastfm.ts new file mode 100644 index 0000000000..11ae6efbed --- /dev/null +++ b/packages/core/src/providers/lastfm.ts @@ -0,0 +1,94 @@ +/** + *
+ * Built-in Last.fm integration. + * + * + * + *
+ * + * @module providers/lastfm + */ +import type { OAuthConfig, OAuthUserConfig } from "./index.js" + +export interface LastFMProfile { + name: string + realname?: string + url: string + image: { "#text": string; size: string }[] +} + +/** + * Add Last.fm login to your page. + * + * ### Setup + * + * #### Callback URL + * ``` + * https://example.com/api/auth/callback/lastfm + * ``` + * + * #### Configuration + *```ts + * import { Auth } from "@auth/core" + * import LastFM from "@auth/core/providers/lastfm" + * + * const request = new Request(origin) + * const response = await Auth(request, { + * providers: [ + * LastFM({ + * clientId: LASTFM_API_KEY, + * clientSecret: LASTFM_SHARED_SECRET, + * }), + * ], + * }) + * ``` + * + * ### Resources + * + * - [Last.fm API docs](https://www.last.fm/api/authentication) + * - [Create an API account](https://www.last.fm/api/account/create) + * + * ### Notes + * + * Last.fm uses a custom authentication flow that is *similar* to OAuth, but requires generating an `api_sig` with your API secret to exchange the temporary token for a session key. + * + * :::info + * + * This provider assumes you handle the session key exchange manually or through a custom callback, since Auth.js does not support MD5 hashing without adding dependencies. + * + * ::: + */ +export default function LastFM

( + options: OAuthUserConfig

+): OAuthConfig

{ + return { + id: "lastfm", + name: "Last.fm", + type: "oauth", + authorization: { + url: "https://www.last.fm/api/auth", + params: { api_key: options.clientId }, + }, + token: "https://ws.audioscrobbler.com/2.0/?method=auth.getSession", + userinfo: { + url: "https://ws.audioscrobbler.com/2.0/?method=user.getInfo&format=json", + async request({ tokens }) { + const res = await fetch( + `https://ws.audioscrobbler.com/2.0/?method=user.getInfo&api_key=${options.clientId}&sk=${tokens.access_token}&format=json` + ) + const { user } = await res.json() + return user + }, + }, + profile(profile) { + return { + id: profile.name, + name: profile.realname || profile.name, + email: null, + image: profile.image?.find((img) => img.size === "large")?.["#text"], + } + }, + style: { brandColor: "#d51007" }, + options, + } +}