Skip to content

feat(providers): Add Epic Games provider #12944

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/ISSUE_TEMPLATE/2_bug_provider.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ body:
- "Discord"
- "Dribbble"
- "Dropbox"
- "Epic Games"
- "Eventbrite"
- "EVE Online"
- "Facebook"
Expand Down
1 change: 1 addition & 0 deletions docs/pages/data/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"dribbble": "Dribbble",
"dropbox": "Dropbox",
"duende-identityserver-6": "DuendeIdentityServer6",
"epicgames": "Epic Games",
"eveonline": "EVE Online",
"faceit": "FACEIT",
"figma": "Figma",
Expand Down
53 changes: 53 additions & 0 deletions docs/public/img/providers/epicgames.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
142 changes: 142 additions & 0 deletions packages/core/src/providers/epicgames.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/**
* <div class="provider" style={{backgroundColor: "#000", display: "flex", justifyContent: "space-between", color: "#fff", padding: 16}}>
* <span>Built-in <b>Epic Games</b> integration.</span>
* <a href="https://store.epicgames.com/">
* <img style={{display: "block"}} src="https://authjs.dev/img/providers/epicgames.svg" height="48" width="48"/>
* </a>
* </div>
*
* @module providers/epicgames
*/

/**
* @provider EpicGames
*
* Epic Games OAuth Provider for Auth.js
*
* This provider uses OAuth 2.0 to authenticate users with their Epic Games account.
*
* @see [Epic Games OAuth Documentation]
* (https://dev.epicgames.com/docs/services/en-US/EpicAccountServices)
* @see [Auth.js OAuth Providers Guide](https://authjs.dev/guides/configuring-oauth-providers)
*
* @example
* ```ts
* import EpicGamesProvider from "next-auth/providers/epicgames"
*
* export default NextAuth({
* providers: [
* EpicGamesProvider({
* clientId: process.env.EPIC_CLIENT_ID,
* clientSecret: process.env.EPIC_CLIENT_SECRET,
* })
* ]
* })
* ```
*
* ### Environment Variables
*
* - `EPIC_CLIENT_ID`
* - `EPIC_CLIENT_SECRET`
*
* ### Profile Response
*
* The Epic Games profile has the following structure:
*
* ```json
* {
* "sub": "user_id_123",
* "preferred_username": "EpicUser123"
* }
* ```
*/

import type { OAuthConfig, OAuthUserConfig } from "./index.js"

/**
* The shape of the user object returned by Epic Games when requesting user info.
* Properties must be adapted based on the exact data the client decides to receive from the Epic Games API.
*/
export interface EpicGamesProfile {
sub: string
preferred_username?: string
// preferred_language?: string;
}

/**
* Configure the Epic Games OAuth provider.
*
* @param {OAuthUserConfig<EpicGamesProfile>} options - User configuration for the Epic Games provider
* @returns {OAuthConfig<EpicGamesProfile>} - An object conforming to the Auth.js OAuthConfig interface
*
* @example
* ```ts
* import EpicGamesProvider from "next-auth/providers/epicgames"
* // ...
* providers: [
* EpicGamesProvider({
* clientId: process.env.EPIC_CLIENT_ID,
* clientSecret: process.env.EPIC_CLIENT_SECRET,
* })
* ]
* ```
*/
export default function EpicGamesProvider<P extends EpicGamesProfile>(
options: OAuthUserConfig<P>
): OAuthConfig<P> {
return {
id: "epicgames",
name: "Epic Games",
type: "oauth",
authorization: {
url: "https://www.epicgames.com/id/authorize",
params: {
scope: "profile friends_list country",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for default scope, let's not include these

Suggested change
scope: "profile friends_list country",
scope: "profile",

response_type: "code",
},
},

token: {
url: "https://api.epicgames.dev/epic/oauth/v2/token",
async request(context) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can simplify this by utilising the property token_endpoint_auth_method: 'client_secret_basic'
no need to specify the fetch call manually here.

const basicAuth = Buffer.from(
`${context.provider.clientId}:${context.provider.clientSecret}`
).toString("base64")

const res = await fetch(
"https://api.epicgames.dev/epic/oauth/v2/token",
{
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${basicAuth}`,
},
body: new URLSearchParams({
grant_type: "authorization_code",
code: context.params.code!,
redirect_uri: context.provider.callbackUrl,
}),
}
)

const tokens = await res.json()
if (!res.ok) throw new Error(JSON.stringify(tokens))

return { tokens }
},
},

userinfo: "https://api.epicgames.dev/epic/oauth/v2/userInfo",

profile(profile: P) {
return {
id: profile.sub,
name: profile.preferred_username ?? profile.sub,
}
},

checks: ["state"],
clientId: options.clientId,
clientSecret: options.clientSecret,
Comment on lines +139 to +140
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no needed to be explicit define these

}
}