Skip to content

Instantly share code, notes, and snippets.

@iXyles
Last active December 4, 2022 07:12
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save iXyles/ec40cb40a2a186425ec6bfb9dcc2ddda to your computer and use it in GitHub Desktop.
Save iXyles/ec40cb40a2a186425ec6bfb9dcc2ddda to your computer and use it in GitHub Desktop.
Simple small OAuth flow for Epicgames new login system, 2FA support
using System;
using System.Linq;
using System.Net;
using Newtonsoft.Json;
using RestSharp;
namespace FNFlowAuthNETCore
{
public class EpicFortniteAuthFlow
{
static void Main(string[] args)
{
new EpicFortniteAuthFlow();
}
public EpicFortniteAuthFlow()
{
var email = "";
var password = "";
Console.WriteLine(GetOAuthToken(email, password));
Console.ReadKey();
}
public string GetOAuthToken(string email, string password, CookieContainer cookieJar = null, string authMethod = null)
{
if (cookieJar == null)
cookieJar = new CookieContainer();
var client = new RestClient("https://www.epicgames.com/id/api/")
{
CookieContainer = cookieJar
};
var csrfRes = client.Execute(new RestRequest("csrf", Method.GET));
var token = csrfRes.Cookies.First(x => x.Name == "XSRF-TOKEN").Value;
var loginRequest = new RestRequest(!string.IsNullOrEmpty(authMethod) ? "login/mfa" : "login", Method.POST)
.AddHeader("Content-Type", "application/x-www-form-urlencoded")
.AddHeader("x-xsrf-token", token);
if (!string.IsNullOrEmpty(authMethod))
{
try
{
Console.Write("Two factor detected, write the 6 number code from 2FA: ");
var authKey = Int32.Parse(Console.ReadLine());
var twoStep = client.Execute(loginRequest
.AddParameter("code", authKey)
.AddParameter("method", authMethod)
.AddParameter("rememberDevice", false));
if (twoStep.StatusCode == HttpStatusCode.BadRequest)
return "WRONG AUTHENTICATED 2AUTH KEY";
}
catch (Exception)
{
return "WRONG AUTHENTICATED 2AUTH KEY";
}
}
else
{
IRestResponse loginRes = client.Execute(loginRequest
.AddParameter("email", email)
.AddParameter("password", password)
.AddParameter("rememberMe", true));
if (loginRes.StatusCode == HttpStatusCode.Conflict)
return GetOAuthToken(email, password, cookieJar);
if (loginRes.StatusCode == HttpStatusCode.RequestHeaderFieldsTooLarge)
return GetOAuthToken(email, password, cookieJar, (string) JsonConvert.DeserializeObject<dynamic>(loginRes.Content)["metadata"].twoFactorMethod);
}
var exchangeRes = client.Execute(
new RestRequest("exchange", Method.POST)
.AddHeader("x-xsrf-token", token));
var oauthClient = new RestClient("https://account-public-service-prod03.ol.epicgames.com/account/api/oauth/token");
var oauthRes = oauthClient.Execute(
new RestRequest(Method.POST)
.AddHeader("Content-Type", "application/x-www-form-urlencoded")
.AddHeader("Authorization", "basic MzQ0NmNkNzI2OTRjNGE0NDg1ZDgxYjc3YWRiYjIxNDE6OTIwOWQ0YTVlMjVhNDU3ZmI5YjA3NDg5ZDMxM2I0MWE=")
.AddParameter("grant_type", "exchange_code")
.AddParameter("exchange_code", JsonConvert.DeserializeObject<dynamic>(exchangeRes.Content)["code"])
.AddParameter("includePerms", true)
.AddParameter("token_type", "eg1"));
return JsonConvert.DeserializeObject<dynamic>(oauthRes.Content)["access_token"];
}
}
}
@skrekhere
Copy link

Hey, i've been trying to follow this authentication flow in my own node.js program, but i've hit a bit of a road bump unfortunately. When trying to request for the exchange token, the api sends back a response of {"errorCode":"errors.com.epicgames.unauthorized","message":"You are not authenticated. Please authenticate."} any ideas as to why this happens?

@iXyles
Copy link
Author

iXyles commented Nov 1, 2019

You're missing the cookies that are needed in the request, check out my requester for my own Node.JS lib: https://github.com/iXyles/fortnite-basic-api/blob/master/src/Requester.js

@skrekhere
Copy link

As far as I can tell, I'm sending all the necessary cookies, if all that's necessary is the x-xsrf-token header, then I sent that one fine.

@PomegranateApps
Copy link

This doesn't seem to work anymore. Did the login flow change again?

@iXyles
Copy link
Author

iXyles commented Jan 11, 2020

They did a modification to the flow yes, if you get a status conflict code as result from the login endpoint, re-do the xsrf (but you've to send the cookies from first login attempt & xsrf) and then do the login again.

PS: This gist got updated yesterday to make it work again, so yes it does work :)

@PomegranateApps
Copy link

I'm having difficulty implementing this for myself but I did verify the changes you made are working. Thanks for making an update!

  1. GET /id/api/csrf and get XSRF-TOKEN
  2. POST /id/api/login and set 'x-xsrf-token' token in header and cookie from #1
  3. login returns errors.com.epicgames.accountportal.session_invalidated on first call
  4. Send Cookie from #1 to /id/api/csrf and get the new XSRF-TOKEN and cookie
  5. POST /id/api/login and set 'x-xsrf-token' to token in header and cookie from #4

I just keep getting the errors.com.epicgames.accountportal.session_invalidated error.

@hamed-estsoft
Copy link

Hello Marcus.
I was searching Google to find a snippet for Epic Games web-based authentication and i came across your Github. I tried to copy exactly what you had but I'm getting an error saying that Authorization code was not found or is invalid. I would really appreciate it if you can help me with this issue. I just don't understand where i need to get the Authorization code. I have been told that it's base64 of client id and client secret. Below is my snippet. Any help is appreciate it.

 var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(CLIENT_ID:CLIENT_SECRET));
                var oauthClient = new RestClient(https://api.epicgames.dev/epic/oauth/v1/token);
                var oauthRes = oauthClient.Execute(
                    new RestRequest(Method.POST)
                        .AddHeader("Content-Type", "application/x-www-form-urlencoded")
                        .AddHeader("Host", "api.epicgames.dev")
                        .AddHeader("Authorization", "Basic "+ credentials)
                        .AddParameter("grant_type", "authorization_code")
                        .AddParameter("deployment_id", "a5aa686defa64131b1edc48c31b40d1a")
                        .AddParameter("scope", "basic_profile")

                        .AddParameter("code", code)
                        .AddParameter("redirect_uri", "https://test:44300/EOSAuth"));
                var res = JsonConvert.DeserializeObject<dynamic>(oauthRes.Content);

@iXyles
Copy link
Author

iXyles commented Dec 10, 2020

Sorry, I am not fully sure that I understand your use-case of what you are trying to do. This is an old gist of how you did it earlier in an easy way. I am not planning to create a gist/version of what you are trying to do above.

@kkm
Copy link

kkm commented Dec 4, 2022

@hamed-estsoft @iXyles

var credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(CLIENT_ID:CLIENT_SECRET));

Client_ID + CLIENT_SECRET both code are stored in binary,
but luckily I can read base64 decode and this is what it looks like:
Authorization Header is present: basic MzQ0NmNkNzI2OTRjNGE0NDg1ZDgxYjc3YWRiYjIxNDE6OTIwOWQ0YTVlMjVhNDU3ZmI5YjA3NDg5ZDMxM2I0MWE= code is above..
Decoded Username:Password= 3446cd72694c4a4485d81b77adbb2141:9209d4a5e25a457fb9b07489d313b41a

so ez..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment