Authorization
A significant portion of Dead by Daylight's private API endpoints require authorization from an existing Dead by Daylight account to be used. Authorization is managed through secure token-based authentication, ensuring that only authenticated users can access the requested resources.
OAuth 2.0
Epic Online Services (EOS) uses the OAuth 2.0 protocol for authentication and authorization, supporting common-use cases for web servers and client-side applications. Epic has also introduced custom grant types for some specific use cases. You can read more about how this process works here.
OAuth Server Endpoints
Endpoint for exchanging an authorization code for an access token. consumingClientId
is the client application that is making the request, codeChallenge
is the value generated from the code verifier (a cryptographically random string) that the client creates, and codeChallengeMethod
is the method used to create the codeChallenge
. EOS uses the SHA-256 hashing algorithm to secure the code challenge in the Proof Key for Code Exchange (PKCE) flow.
Endpoint for obtaining an access token, refresh token, and other identity information after an authorization code has been exchanged.
Endpoint for retrieving detailed information about a given access token. When a client sends a valid access token, the endpoint returns metadata about that token.
Endpoint that allows clients to implement a "log out" feature by invalidating tokens, ensuring that any security credentials or access rights associated with the session are removed. This endpoint implements RFC 7009 - OAuth 2.0 Token Revocation.
Errors
The endpoints above will return error messages according to the relevant RFC.
Client Types
OAuth defines two types of clients: confidential clients and public clients.
Confidential Clients
Confidential clients are applications that can securely authenticate with the authorization server. In OAuth 2.0, this involves both a client ID and a client secret.
The client ID is a public identifier for applications registered with an OAuth 2.0 authorization server. The client ID must be unique across all clients managed by the authorization server to ensure that requests can be correctly attributed to the appropriate application.
The client secret is a confidential credential that is known exclusively to both the application and the authorization server. It acts as the application's password, playing a crucial role in ensuring secure communication between the application and the server.
These clients may use the authorization_code
, client_credentials
, exchange_code
, password
, and refresh_token
grant types.
Confidential clients can have ID tokens issued to them either symmetrically using their client secret (HS256) or asymmetrically using a private key (RS256) because they can securely store secrets.
Public Clients
Public clients, such as applications running in a browser or on a mobile device, cannot use registered client secrets.
These clients may only use grant types that do not require the use of their client secret. They cannot send a client secret because they can't maintain the confidentiality of the credentials required.
Because public clients cannot securely hold secrets, ID tokens issued to them must be signed asymmetrically using a private key (RS256) and verified using the corresponding public key.
Tokens
When attempting to play Dead by Daylight, the client interacts with Epic Games' OAuth 2.0 authentication system, which provides access tokens, refresh tokens, and ID tokens.
Access Token
An access token is issued by the authorization server to allow the client to access protected resources on behalf of the user. This type of token lasts for 2 hours from the time that it is issued.
Refresh Token
A refresh token is used to obtain a new access token without requiring the user to re-authenticate. This type of token lasts for 8 hours from the time that it is issued.
ID Token
An ID token is part of the OpenID Connect protocol, which provides a standardized way to authenticate and retrieve basic user information, and is used to authenticate the user's identity. It contains user information in the form of claims (for example, Epic Account ID, Epic Account display name, client ID used to authenticate the user with Epic Account Services, and other identity details). This type of token lasts for 2 hours from the time that it is issued.
Available Scopes
When the client attempts to connect to Dead by Daylight's servers, it requests specific scopes as part of the OAuth 2.0 authorization flow. The scope "basic_profile friends_list openid presence"
defines the resources and permissions the client requires:
basic_profile
: Allows the client to authenticate the user with Epic Account Services and access basic profile information like the user's screen name. It may also allow certain account actions like making in-game purchases, but the specifics depend on the service integration.friends_list
: Grants the client access to the user's list of friends to display in-game, which could be used for social features or multiplayer connections.openid
: Allows the client to request an ID token.presence
: Allows the client to access and display information about both the user's basic status and game-specific status. Basic status includes generic information such as whether the user is online or offline, what game they are playing (provided the user has agreed to share information about that game), and so on. Game-specific status contains information particular to the game the user is playing, if any.Grant Types
Authorization Code Grant
This grant is used by confidential and public clients to exchange an authorization code for an access token. After the user returns to the client via the redirect URL, the application will get the authorization code from the URL and use it to request an access token. You can read about its flow here.
Note that authenticating with this method only works on clients that have a redirect URL configured. There is no redirect URL configured for the Dead by Daylight client, so I will use this method with fortnitePCGameClient
to explain how this process works. Importantly, jaren.wtf's unofficial Epic Games Client Documentation shows that fortnitePCGameClient
's client ID is ec684b8c687f479fadea3cb2ad83f5c6
and its client secret is e1f31c211f28413186262d37a13fc84d
.
Method
1. Determine the client that you want to get an access token for.
2. Ensure that you are logged in at Epic Games.
3. Open https://www.epicgames.com/id/api/redirect?clientId=:clientId&responseType=code in your web browser, replacing :clientId
with the chosen client ID. In this case, that would be ec684b8c687f479fadea3cb2ad83f5c6
.
4. If performed successfully, then you will receive a response with the following structure:
{
"redirect_Url": string,
"authorizationCode": string,
"exchangeCode": null,
"sid": null,
"ssoV2Enabled": boolean
}
Key | Type | Description |
---|---|---|
redirect_Url |
String | The URL where the user will be redirected after authentication. It contains the authorization code as a query parameter, which the client can later exchange for an access token. |
authorizationCode |
String | A temporary authorization code issued after the user successfully logs in. This code is used in the next step to request an access token. |
exchangeCode |
String (can be null) | A code used to exchange for an access token in specific OAuth flows. This field is null if the exchange code hasn't been issued yet or isn't required for this flow. |
sid |
String (can be null) | Represents the session ID (SID) for the user's authentication session. It is null if not applicable or not yet assigned during this process. |
ssoV2Enabled |
Boolean | Indicates whether Single Sign-On (SSO) version 2 is enabled for this authentication flow. If it is true, then the system supports SSOv2, meaning that the user can authenticate across multiple services without re-entering credentials. |
Note that the authorization code expires after 5 minutes. If step 5 is not completed within 5 minutes of following step 3, then you will have to generate a new authorization code.
5. Send a POST
request to https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token:
Request Headers:
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {client_id:client_secret}
Note that client_id:client_secret
needs to be encoded in Base64.
Request Body:
grant_type=authorization_code&code={authorizationCode}
Note that authorizationCode
comes from the response in step 4.
Example: Authenticating to fortnitePCGameClient
Request Headers:
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZWM2ODRiOGM2ODdmNDc5ZmFkZWEzY2IyYWQ4M2Y1YzY6ZTFmMzFjMjExZjI4NDEzMTg2MjYyZDM3YTEzZmM4NGQ=
Request Body:
grant_type=authorization_code&code={authorizationCode}
If performed successfully, then you will receive a response with the following structure:
{
"access_token": string,
"expires_in": 7200,
"expires_at": string,
"token_type": "bearer",
"refresh_token": string,
"refresh_expires": 28800,
"refresh_expires_at": string,
"account_id": string,
"client_id": "ec684b8c687f479fadea3cb2ad83f5c6",
"internal_client": true,
"client_service": "prod-fn",
"scope": [
"basic_profile"
],
"displayName": string,
"app": "prod-fn",
"in_app_id": string,
"device_id": string,
"product_id": "prod-fn",
"application_id": string,
"acr": "urn:epic:loa:aal2",
"auth_time": string
}
Key | Type | Description |
---|---|---|
access_token |
String | The OAuth 2.0 access token that grants temporary access to the application resources. |
expires_in |
Number | The time (in seconds) until the access token expires. |
expires_at |
String | The timestamp (in ISO 8601 format) indicating when the access token will expire. |
token_type |
String | Specifies the token type. |
refresh_token |
String | The token used to obtain a new access token once the original expires. |
refresh_expires |
Number | The time (in seconds) until the refresh token expires. |
refresh_expires_at |
String | The timestamp (in ISO 8601 format) indicating when the refresh token will expire. |
account_id |
String | The ID of the user's account in Epic Games services. |
client_id |
String | The ID of the client making the request, which is typically the application. |
internal_client |
Boolean | Indicates if the client is internal to Epic systems. |
client_service |
String | The service related to the client. |
scope |
Array | The permissions granted to the token, detailing what the client can access. |
displayName |
String | The display name of the user associated with the account. |
app |
String | The application requesting the token. |
in_app_id |
String | The user's ID specific to the application (used within the app). |
device_id |
String | The ID of the device being used for the request. |
product_id |
String | The ID of the product the token is associated with. |
application_id |
String | The ID of the application requesting the token. This may differ from client_id in cases where multiple applications share the same client. |
acr |
String | The strength of the authentication used. |
auth_time |
String | The timestamp (in ISO 8601 format) indicating when authentication occurred. |
Client Credentials Grant
This grant is used by clients to obtain an access token outside of the context of a user. This is used by clients to access resources about themselves rather than to access a user's resources. You can read about its flow here.
Method
1. Determine the client that you want to get an access token for.
2. Send a POST
request to https://api.epicgames.dev/auth/v1/oauth/token:
Request Headers:
Content-Type: application/x-www-form-urlencoded
Authorization: Basic {client_id:client_secret}
Note that client_id:client_secret
needs to be encoded in Base64.
Request Body:
grant_type=client_credentials
Example: Authenticating to Dead by Daylight Client
You can recover both the client ID and client secret from the Dead by Daylight client by capturing its network traffic as you launch the game from the Epic Games Launcher. Specifically, if you observe the "Authorization" request header of any requests sent to https://api.epicgames.dev/auth/v1/oauth/token or https://api.epicgames.dev/epic/oauth/v2/token, then you will see that the header value is Basic eHl6YTc4OTFzSGQ4NDdVeEI0cG54WjMwT2hRRXFNZXQ6SXdYNEg4bmRDbmE0R1RpRDhMVFkyT3hqSDJncG5uYkt6cTRCaHFkL250RQ==
. You can use this website to decode the Base64-encoded text. In this case, eHl6YTc4OTFzSGQ4NDdVeEI0cG54WjMwT2hRRXFNZXQ6SXdYNEg4bmRDbmE0R1RpRDhMVFkyT3hqSDJncG5uYkt6cTRCaHFkL250RQ==
decodes into xyza7891sHd847UxB4pnxZ30OhQEqMet:IwX4H8ndCna4GTiD8LTY2OxjH2gpnnbKzq4Bhqd/ntE
, meaning that the client ID is xyza7891sHd847UxB4pnxZ30OhQEqMet
and the client secret is IwX4H8ndCna4GTiD8LTY2OxjH2gpnnbKzq4Bhqd/ntE
.
Request Headers:
Content-Type: application/x-www-form-urlencoded
Authorization: Basic eHl6YTc4OTFzSGQ4NDdVeEI0cG54WjMwT2hRRXFNZXQ6SXdYNEg4bmRDbmE0R1RpRDhMVFkyT3hqSDJncG5uYkt6cTRCaHFkL250RQ==
Request Body:
grant_type=client_credentials
If performed successfully, then you will receive a response with the following structure:
{
"access_token": string,
"token_type": "bearer",
"expires_at": string,
"features": [
"Achievements",
"AntiCheat",
"Ecom",
"Matchmaking",
"Metrics",
"PlayerReports",
"Sanctions",
"Stats"
],
"organization_id": "o-2ctdycbd3jxm6a26vpz5d4xv872smu",
"product_id": "2b2299be8ae84d679d4dc57c55af1510",
"expires_in": 3599
}
Key | Type | Description |
---|---|---|
access_token |
String | The OAuth 2.0 access token that grants temporary access to the application resources. |
token_type |
String | Specifies the token type. |
expires_at |
String | The timestamp (in ISO 8601 format) indicating when the access token will expire. |
features |
Array | A list of features enabled for the access token. |
organization_id |
String | A unique identifier for the organization associated with the token. |
product_id |
String | A unique identifier for the product associated with the token. |
expires_in |
Number | The time (in seconds) until the access token expires. |
Exchange Code Grant
This grant allows a client to exchange a short-lived authorization code for an access token. Note that, because Epic has removed the endpoint that made it possible to get an exchange code through a browser, the only way to get an exchange code at this point is to launch the selected game using the Epic Games Launcher. That being said, you can still recover the access token by capturing the network traffic of a game as you launch it from the Epic Games Launcher. Specifically, if you observe the request sent to https://api.epicgames.dev/epic/oauth/v2/token after launching the game, then you will see a response with the following structure:
{
"scope": string,
"token_type": string,
"access_token": string,
"refresh_token": string,
"id_token": string,
"expires_in": number,
"expires_at": string,
"refresh_expires_in": number,
"refresh_expires_at": string,
"account_id": string,
"client_id": string,
"application_id": string,
"selected_account_id": string,
"merged_accounts": [],
"acr": string,
"auth_time": string
}
Key | Type | Description |
---|---|---|
scope |
String | The permissions granted to the token, detailing what the client can access. |
token_type |
String | Specifies the token type. |
access_token |
String | The OAuth 2.0 access token that grants temporary access to the application resources. |
refresh_token |
String | The token used to obtain a new access token once the original expires. |
id_token |
String | A token that carries information about the user in the form of claims. |
expires_in |
Number | The time (in seconds) until the access token expires. |
expires_at |
String | The timestamp (in ISO 8601 format) indicating when the access token will expire. |
refresh_expires_in |
Number | The time (in seconds) until the refresh token expires. |
refresh_expires_at |
String | The timestamp (in ISO 8601 format) indicating when the refresh token will expire. |
account_id |
String | The ID of the user's account in Epic Games services. |
client_id |
String | The ID of the client making the request, which is typically the application. |
application_id |
String | The ID of the application requesting the token. This may differ from client_id in cases where multiple applications share the same client. |
selected_account_id |
String | The account ID that was chosen if the user has multiple accounts linked to the service. |
merged_accounts |
Array | An array of account IDs that were merged into the primary account. |
acr |
String | The strength of the authentication used. |
auth_time |
String | The timestamp (in ISO 8601 format) indicating when authentication occurred. |
Example: Authenticating to Dead by Daylight Client
Request Headers:
Host: api.epicgames.dev,
Accept-Encoding: identity
Content-Type: application/x-www-form-urlencoded
Accept: application/json
Authorization: Basic eHl6YTc4OTFzSGQ4NDdVeEI0cG54WjMwT2hRRXFNZXQ6SXdYNEg4bmRDbmE0R1RpRDhMVFkyT3hqSDJncG5uYkt6cTRCaHFkL250RQ==
X-Epic-Correlation-ID: {X-Epic-Correlation-ID}
User-Agent: EOS-SDK/{X-EOS-Version} ({os_name}/{os_version}.{os_build}.{architecture}) DeadByDaylight/{version}
X-EOS-Version: {sdk_version}-{build_number}
Content-Length: {Content-Length}
where X-Epic-Correlation-ID
is a unique identifier used to track requests across Epic's services, X-EOS-Version
is the EOS SDK version followed by the build number, os_name
is the name of the operating system, os_version
is the version number of the operating system, os_build
is the the specific build of the operating system, architecture
is the system's architecture, version
is the version of the game, sdk_version
is the EOS SDK version, build_number
is the EOS SDK build number, and Content-Length
is the size of the request body in bytes.
Request Body:
grant_type=exchange_code&scope=basic_profile+friends_list+presence+openid&exchange_code={exchange_code}&deployment_id={deployment_id},
where exchange_code
is the exchange code and deployment_id
is a unique identifier that represents a specific game deployment integrated with EOS.
Response:
{
"scope": "basic_profile friends_list openid presence",
"token_type": "bearer",
"access_token": string,
"refresh_token": string,
"id_token": string,
"expires_in": 7200,
"expires_at": string,
"refresh_expires_in": 28800,
"refresh_expires_at": string,
"account_id": string,
"client_id": "xyza7891sHd847UxB4pnxZ30OhQEqMet",
"application_id": string,
"selected_account_id": string,
"merged_accounts": [],
"acr": "AAL1",
"auth_time": string
}
Refresh Token Grant
This grant is used by clients to exchange a refresh token for an access token when the access token has expired. This allows clients to continue to have a valid access token without further interaction with the user. You can read about its flow here.
Method for Authenticating to Dead by Daylight Client
1. Determine the client that you want to get a refresh token for.
2. In general, you will receive a refresh_token
property at the final step of the authorization code grant. However, as discussed previously, you cannot authenticate to Dead by Daylight using this grant. Instead, in the case of Dead by Daylight, you will receive a refresh_token
property at the final step of the exchange code grant. This token lasts for 8 hours and can be exchanged to generate a new access token without requesting the user's consent again.
3. Launch Dead by Daylight using the Epic Games Launcher while your network traffic capture application is running.
4. Once you load to the title screen (where Dead by Daylight has you "PRESS SPACE TO CONTINUE"), return to your network traffic capture application.
5. Find the session associated with the exchange_code
grant_type
(this will be the request sent to https://api.epicgames.dev/epic/oauth/v2/token).
6. In the response body, you will see the refresh_token
key and its associated value. Right-click on it and left-click on "Copy".
7. Send a POST
request to https://account-public-service-prod.ol.epicgames.com/account/api/oauth/token:
Request Headers:
Content-Type: application/x-www-form-urlencoded
Authorization: Basic eHl6YTc4OTFzSGQ4NDdVeEI0cG54WjMwT2hRRXFNZXQ6SXdYNEg4bmRDbmE0R1RpRDhMVFkyT3hqSDJncG5uYkt6cTRCaHFkL250RQ==
Request Body:
grant_type=refresh_token&refresh_token={refresh_token}
Note that refresh_token
comes from the response in step 6.
8. If performed successfully, then you will receive a response with the following structure:
{
"access_token": string,
"expires_in": 7200,
"expires_at": string,
"token_type": "bearer",
"refresh_token": string,
"refresh_expires": 28800,
"refresh_expires_at": string,
"account_id": string,
"client_id": "xyza7891sHd847UxB4pnxZ30OhQEqMet",
"internal_client": false,
"client_service": "2b2299be8ae84d679d4dc57c55af1510",
"scope": [
"basic_profile",
"friends_list",
"openid",
"presence"
],
"displayName": string,
"app": "2b2299be8ae84d679d4dc57c55af1510",
"in_app_id": string,
"device_id": string,
"product_id": "2b2299be8ae84d679d4dc57c55af1510",
"sandbox_id": "611482b8586142cda48a0786eb8a127c",
"deployment_id": string,
"application_id": string,
"acr": "urn:epic:loa:aal1",
"auth_time": string
}
Key | Type | Description |
---|---|---|
access_token |
String | The OAuth 2.0 access token that grants temporary access to the application resources. |
expires_in |
Number | The time (in seconds) until the access token expires. |
expires_at |
String | The timestamp (in ISO 8601 format) indicating when the access token will expire. |
token_type |
String | Specifies the token type. |
refresh_token |
String | The token used to obtain a new access token once the original expires. |
refresh_expires |
Number | The time (in seconds) until the refresh token expires. |
refresh_expires_at |
String | The timestamp (in ISO 8601 format) indicating when the refresh token will expire. |
account_id |
String | The ID of the user's account in Epic Games services. |
client_id |
String | The ID of the client making the request, which is typically the application. |
internal_client |
Boolean | Indicates if the client is internal to Epic systems. |
client_service |
String | The service related to the client. |
scope |
Array | The permissions granted to the token, detailing what the client can access. |
displayName |
String | The display name of the user associated with the account. |
app |
String | The application requesting the token. |
in_app_id |
String | The user's ID specific to the application (used within the app). |
device_id |
String | The ID of the device being used for the request. |
product_id |
String | The ID of the product the token is associated with. |
sandbox_id |
String | The environment the API requests and responses belong to. |
deployment_id |
String | A unique identifier that represents a specific game deployment integrated with EOS. |
application_id |
String | The ID of the application requesting the token. This may differ from client_id in cases where multiple applications share the same client. |
acr |
String | The strength of the authentication used. |
auth_time |
String | The timestamp (in ISO 8601 format) indicating when authentication occurred. |