Authentication

While some endpoints can be freely accessed, referred to as public endpoints, others require you to have authenticated with bungie.net, referred to as private endpoints. In order to access these, your client, which may be a web browser, desktop application, or web site, needs to sign in to a bungie.net account using a supported login provider.

## Supported Login Providers

The following login providers can be used to create and sign in to a bungie.net account:

* PlayStation Network (PSN)
* Microsoft Account (Xbox)

## Overview

In a high-level, broad overview, there are two steps to follow in order to be authorised.

### **Authenticate with login provider**

In this step, your client provides their login details to their respective provider.

1. Request the provider's login form
2. Insert login details into the form (i.e., email address and password)
3. Send the form back to the provider along with any other expected values
4. Store cookies

### **Authenticate with bungie.net**

1. Send a request to the sign in link on bungie.net which corresponds to the provider (these can be found under the Sign In dropdown on bungie.net)
2. Allow any redirects to occur and provide the appropriate cookies for the login provider when required
3. Continue to allow redirection
4. Store bungie.net cookies

When both steps are complete, you will be able to make requests to private endpoints by including additional information in your requests.

## Fair Warning

Officially, Bungie is not providing support for private endpoints nor authentication. Additionally, your application should not be requesting users to provide their login details if these are to be transmitted to a third-party, such as your own website or database. The only location they should be transmitted to is their respective login provider.

## Authentication Scripts

Below is a list of scripts, excerpts, and programs provided by community members to automate the authentication flow. Use at your own risk.

### **BASH**

#### **"Simple" authentication using PSN**

```
#!/bin/bash
# Enable "strict" mode (see http://redsymbol.net/articles/unofficial-bash-strict-mode/)
set -euo pipefail
IFS=$'\n\t'

# NOTE THAT DOING SO WILL CAUSE YOUR PASSWORD TO BE WRITTEN TO THE SCREEN

PSN\_OAUTH\_BASE="https://auth.api.sonyentertainmentnetwork.com/2.0/oauth/authorize"
PSN\_OAUTH\_RESPONSE\_TYPE="code"
PSN\_OAUTH\_CLIENT\_ID="78420c74-1fdf-4575-b43f-eb94c7d770bf"
PSN\_OAUTH\_REDIRECT\_URI="https://www.bungie.net/en/User/SignIn/Psnid"
PSN\_OAUTH\_SCOPE="psn:s2s"
PSN\_REQUEST\_LOCALE="en"

# cURL produces a line like...
#HttpOnly\_auth.api.sonyentertainmentnetwork.com FALSE / TRUE 0 JSESSIONID 00000000000000000000000000000000.lvp-p1-npversat12-4709
#-s for silent so it doesn't show a progress bar
#"--cookie-jar -" causes cookies to be written to output so awk can see them.
#awk is then used to pull the value of JSESSIONID out.
LOGIN\_SESSION\_COOKIE="JSESSIONID="$(
 curl -G -s --cookie-jar - ${PSN\_OAUTH\_BASE} \
 --data-urlencode "response\_type=${PSN\_OAUTH\_RESPONSE\_TYPE}" \
 --data-urlencode "client\_id=${PSN\_OAUTH\_CLIENT\_ID}" \
 --data-urlencode "redirect\_uri=${PSN\_OAUTH\_REDIRECT\_URI}" \
 --data-urlencode "scope=${PSN\_OAUTH\_SCOPE}" \
 --data-urlencode "locale=${PSN\_REQUEST\_LOCALE}" \

# Read the PSN password, but don't print it out
stty -echo
read -p "PSN Password: " PSN_PASSWORD; echo
stty echo

PSN\_LOGIN\_URI="https://auth.api.sonyentertainmentnetwork.com/login.do"

# yields a new cookie value.
LOGIN\_COMPLETE\_COOKIE="JSESSIONID="$(
 curl -s --cookie-jar - ${PSN\_LOGIN\_URI} \
 -H "Cookie: ${LOGIN\_SESSION\_COOKIE}" \
 --data-urlencode "j\_username=${PSN\_USERNAME}" \
 --data-urlencode "j\_password=${PSN\_PASSWORD}" \

# Headers contain "\r\n" so we have to trim the last character.
BUNGIE\_LOGIN\_URI=$(
 curl -G -s -D - ${PSN\_OAUTH\_BASE} \
 -H "Cookie: ${LOGIN\_COMPLETE\_COOKIE}" \
 --data-urlencode "response\_type=${PSN\_OAUTH\_RESPONSE\_TYPE}" \
 --data-urlencode "client\_id=${PSN\_OAUTH\_CLIENT\_ID}" \
 --data-urlencode "redirect\_uri=${PSN\_OAUTH\_REDIRECT\_URI}" \
 --data-urlencode "scope=${PSN\_OAUTH\_SCOPE}" \
 --data-urlencode "locale=${PSN\_REQUEST\_LOCALE}" \

# Making a request to signin yields a lot of cookies
curl -s --cookie-jar - ${BUNGIE\_LOGIN\_URI} \

```
by abl

### **Java**

#### **BungieLoginSingleton in Java**

```
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.JSONObject;

/\*\*
 \* Singleton to use for Bungie login, cookiestore remains the same for the session with cookies stored
 \* @author MilkazarDevs
 \*
 \*
 \*
 \*/
public class BungieLoginSingleton {

 private final static String X\_API\_KEY = "YOUR API KEY HERE";

 private final static int TYPE\_XBOX = 1;
 private final static int TYPE\_PSN = 2;

 private final static String BURL = "https://www.bungie.net/en/User/SignIn/";
 private final static String BURL\_NO\_LOC = "/User/SignIn/";

 private final static String PSN = "Psnid";
 private final static String PSN\_LOGIN\_URL = "https://auth.api.sonyentertainmentnetwork.com/login.do";

 private final static String XBOX = "Xuid";

 private final static String USERAGENT = "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.125 Safari/537.36";
 private final static String BUNGLED\_COOKIE\_NAME = "bungled";
 private final static String BUNGLEME\_COOKIE\_NAME = "bungleme";

 private static BungieLoginSingleton mInstance;
 private int type;

 private boolean loggedIn = false;
 private HttpURLConnection urlConnection;
 private final CookieManager cookieManager;
 private String X\_CSRF = null;
 private String ACCOUNT\_ID = null;

 public BungieLoginSingleton(){
 cookieManager = new CookieManager(null, CookiePolicy.ACCEPT\_ALL);
 CookieHandler.setDefault(cookieManager);
 }

 public static synchronized BungieLoginSingleton getInstance(){
 if (mInstance == null) {
 mInstance = new BungieLoginSingleton();
 }
 return mInstance;
 }

 public boolean checkLogin(String uname, String pword, int provider) throws IOException{
 this.type = provider;
 //check login
 checkLoginFlow(uname, pword, provider);

 if(loggedIn){
 //force cookiestore population
 urlConnection.getHeaderFields();
 //save header values
 readCookiesForHeaderValues();
 log("X\_CSRF: "+X\_CSRF);
 log("ACC\_ID: "+ACCOUNT\_ID);
 }

 printHeaders();
 printCookies();

 testLogin();

 return loggedIn;
 }

 private void checkLoginFlow(String uname, String pword, int provider) throws IOException{

 log("\*\*\*\*\*\*\*\*\*\*\*checkloginFlow started");
 //form url based on provider
 String urlProvider = provider==TYPE\_XBOX ? BURL+XBOX : BURL+PSN;
 //check cookie validity
 urlConnection = (HttpURLConnection) new URL(urlProvider).openConnection();
 urlConnection.setInstanceFollowRedirects(false);
 urlConnection.addRequestProperty("User-Agent", USERAGENT);

 String newLoc = urlConnection.getHeaderField("Location");
 log("Loc1: "+newLoc);

 //Bungie cookies still valid (also check if there's a locale redirect (/en/User/SignIn/ could become fr/User/SignIn, it/User/Signin etc)
 if(newLoc==null || newLoc.endsWith(BURL\_NO\_LOC+(provider==TYPE\_XBOX ? XBOX : PSN))){
 loggedIn = true;
 return;
 }

 urlConnection = (HttpURLConnection) new URL(newLoc).openConnection();
 urlConnection.setInstanceFollowRedirects(false);
 urlConnection.addRequestProperty("User-Agent", USERAGENT);

 if(urlConnection.getResponseCode()==HttpURLConnection.HTTP\_OK){
 //XBOX doesn't redirect, get current url
 newLoc = urlConnection.getURL().toString();
 }else{
 //psn redirects get redirection url
 newLoc = urlConnection.getHeaderField("Location");
 }
 log("Loc2: "+newLoc);
 //if it contains ?code, it's authenticated
 if(!newLoc.contains(urlProvider+"?code")){
 loggedIn = false;
 //no need to try login with uname or pword empty
 if(uname.equals("") || pword.equals("")){
 return;
 }

 switch(provider){
 case TYPE\_XBOX:
 //get xbox auth url, passing right params
 String xboxLoginUrl = "https://login.live.com/ppsecure/post.srf?" + newLoc.substring(newLoc.indexOf("?")+1);
 log("XboxLoginUrl: "+xboxLoginUrl);
 //read content, need to look for input value with id i0327 to pass as parameter
 BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
 StringBuffer content = new StringBuffer();
 String inputLine;
 while((inputLine = in.readLine()) != null)
 content.append(inputLine);
 in.close();
 //search ppftvalue
 Matcher matcher = Pattern.compile("id\\=\"i0327\" value\\=\"(.\*?)\"\\/").matcher(content);
 log("Content: \n"+content.toString());
 if(matcher.find()){
 String ppft = matcher.group(1);
 log("Value ppft: "+ppft);

 matcher = Pattern.compile("urlPost:'(.\*?)'").matcher(content);
 //something changed, it wasn't working no more, with this fix it works again
 if(matcher.find()){
 xboxLoginUrl = matcher.group(1);
 }
 //do request
 Map<String, String> map = new HashMap<String, String>();
 map.put("PPFT", ppft);
 map.put("login", uname);
 map.put("passwd", pword);
 map.put("KMSI","1");
 String qParams = createURLEncodedQueryString(map);

 urlConnection = (HttpURLConnection) new URL(xboxLoginUrl).openConnection();
 urlConnection.addRequestProperty("User-Agent", USERAGENT);
 urlConnection.setInstanceFollowRedirects(true);
 urlConnection.setRequestMethod("POST");
 //urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
 urlConnection.setDoOutput(true);
 urlConnection.setFixedLengthStreamingMode(qParams.getBytes().length);
 PrintWriter out = new PrintWriter(urlConnection.getOutputStream());
 out.print(qParams);
 out.close();

 //follow redirects seems not to always work here
 newLoc = urlConnection.getHeaderField("Location");
 if(newLoc==null){
 newLoc = urlConnection.getURL().toString();
 }else{
 //do the redirect manually
 urlConnection = (HttpURLConnection) new URL(newLoc).openConnection();
 urlConnection.setInstanceFollowRedirects(true);
 urlConnection.addRequestProperty("User-Agent", USERAGENT);

 newLoc = urlConnection.getURL().toString();
 }
 log("XboxLoc: "+newLoc);

 if(newLoc.contains(urlProvider+"?code")){
 loggedIn = true;
 return;
 }

 loggedIn = false;
 return;

 }
 log("No match found. No login");
 loggedIn = false;
 return;

 case TYPE\_PSN:

 Map<String, String> map = new HashMap<String, String>();
 map.put("params", "cmVxdWVzdF9sb2NhbGU9ZW5fVVMmcmVxdWVzdF90aGVtZT1saXF1aWQ=");
 map.put("j\_username", uname);
 map.put("j\_password", pword);
 map.put("rememberSignIn","1");
 String qParams = createURLEncodedQueryString(map);

 urlConnection = (HttpURLConnection) new URL(PSN\_LOGIN\_URL).openConnection();
 urlConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
 urlConnection.setInstanceFollowRedirects(false);
 urlConnection.setDoOutput(true);
 urlConnection.setRequestMethod("POST");
 urlConnection.addRequestProperty("User-Agent", USERAGENT);
 urlConnection.setFixedLengthStreamingMode(qParams.getBytes().length);
 PrintWriter out = new PrintWriter(urlConnection.getOutputStream());
 out.print(qParams);
 out.close();

 newLoc = urlConnection.getHeaderField("Location");
 log("Loc3: "+newLoc);
 if(newLoc.contains("authentication\_error")){
 log("Auth error psn");
 loggedIn = false;
 return;
 }

 urlConnection = (HttpURLConnection) new URL(newLoc).openConnection();
 urlConnection.setInstanceFollowRedirects(true);
 urlConnection.addRequestProperty("User-Agent", USERAGENT);
 urlConnection.setRequestMethod("GET");

 newLoc = urlConnection.getHeaderField("Location");
 log("Loc4: "+newLoc);

 break;
 }

 if(newLoc==null){
 newLoc = urlConnection.getURL().toString();
 log("Loc4.1: "+newLoc);
 }

 if(newLoc.toLowerCase().contains("register")){
 loggedIn = false;
 return;
 }

 loggedIn = newLoc.indexOf(urlProvider) == 0;
 return;
 }

 urlConnection = (HttpURLConnection) new URL(newLoc).openConnection();
 urlConnection.setInstanceFollowRedirects(false);
 urlConnection.addRequestProperty("User-Agent", USERAGENT);

 urlConnection.connect();

 loggedIn = true;
 }

 private void readCookiesForHeaderValues() {

 Iterator<HttpCookie> cooks = cookieManager.getCookieStore().getCookies().iterator();
 while(cooks.hasNext()/\* && X\_CSRF==null && ACCOUNT\_ID==null\*/){
 HttpCookie cook = cooks.next();
 log(cook.toString());
 if(cook.getName().equals(BUNGLED\_COOKIE\_NAME)){
 X\_CSRF = cook.getValue();
 }
 if(cook.getName().equals(BUNGLEME\_COOKIE\_NAME)){
 ACCOUNT\_ID = cook.getValue();
 }
 }
 }

 //TODO remove, just for testing
 private void log(String message){
 System.out.println(message);
 }
 //TODO remove, just for testing
 private void printCookies(){
 System.out.println("\*\*\*\*\*\*Cookies\*\*\*\*\*\*\* ");
 for(HttpCookie co : cookieManager.getCookieStore().getCookies()){
 log("\t"+co.getName()
 + "\t"+co.getValue()
 + "\t"+co.getMaxAge());
 }
 }
 //TODO remove, just for testing
 private void printHeaders(){
 Set<Entry<String, List<String>>> entrySet = urlConnection.getHeaderFields().entrySet();
 System.out.println("\*\*\*\*\*\*Headers\*\*\*\*\*\*\* ");
 for(Entry<String, List<String>> entry : entrySet){
 System.out.println("\t"+entry.getKey()+"\t"+entry.getValue().toString());
 }
 }

 private static final char PARAMETER\_DELIMITER = '&';
 private static final char PARAMETER\_EQUALS\_CHAR = '=';
 /\*\*
 \*
 \* @param parameters a Map containing Key,Value pairs
 \* @return a String representing the url encoded querystring
 \* @throws UnsupportedEncodingException
 \*/
 public static String createURLEncodedQueryString(Map<String, String> parameters) throws UnsupportedEncodingException {
 StringBuilder parametersAsQueryString = new StringBuilder();
 if (parameters != null) {
 boolean firstParameter = true;

 for (String parameterName : parameters.keySet()) {
 if (!firstParameter) {
 parametersAsQueryString.append(PARAMETER\_DELIMITER);
 }
 parametersAsQueryString.append(parameterName)
 .append(PARAMETER\_EQUALS\_CHAR)
 .append(URLEncoder.encode(
 parameters.get(parameterName), "UTF-8"));

 firstParameter = false;
 }
 }
 return parametersAsQueryString.toString();
 }

 private void testLogin() throws IOException{
 String url = "https://www.bungie.net/Platform/Destiny/"+type+"/MyAccount/Vault/";

 log(url);

 urlConnection = (HttpURLConnection) new URL(url).openConnection();
 urlConnection.setRequestMethod("GET");
 urlConnection.addRequestProperty("User-Agent", USERAGENT);
 urlConnection.addRequestProperty("X-API-Key", X\_API\_KEY);
 urlConnection.addRequestProperty("X-CSRF", X\_CSRF);

 BufferedReader in = new BufferedReader(
 new InputStreamReader(urlConnection.getInputStream())
 );

 String inputLine;
 StringBuffer sbTmp = new StringBuffer();
 while ((inputLine = in.readLine()) != null)
 sbTmp.append(inputLine);
 in.close();

 JSONObject json = new JSONObject(sbTmp.toString()); // convert it to JSON object
 log(json.toString(4));
 }
}

```
by Milkazar

### **PHP**

#### **Destiny PHP cURL Authentication**

```
function do\_webauth($method, $username, $password) {
	global $cookie\_file; // a valid file path to where the cookies will be stored (ie cookie.txt)
	$user\_agent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1";

	$methods = array(
		'psn' => 'Psnid',
		'xbox' => 'Xuid'//'Wlid'
	);
	$dest = 'Wlid'; if (isset($methods[$method])) $dest = $methods[$method];
	$url = BUNGIE\_URL.'/en/User/SignIn/'.$dest;

	$default\_options = array(
		CURLOPT\_USERAGENT => $user\_agent,
		CURLOPT\_COOKIEJAR => $cookie\_file,
		CURLOPT\_COOKIEFILE => $cookie\_file,
		CURLOPT\_RETURNTRANSFER => true,
		CURLOPT\_SSL\_VERIFYHOST => 2,
	);

	// Get Third Party Authorization URL
	$ch = curl\_init();
	curl\_setopt\_array($ch, $default\_options);
	curl\_setopt\_array($ch, array(
		CURLOPT\_URL => $url,
	));
	curl\_exec($ch);
	$redirect\_url = curl\_getinfo($ch)['redirect\_url'];
	curl\_close($ch);

	// Bungie Cookies are still valid
	if (!$redirect\_url) return true;

	// Try to authenticate with Third Party
	$ch = curl\_init();
	curl\_setopt\_array($ch, $default\_options);
	curl\_setopt\_array($ch, array(
		CURLOPT\_URL => $redirect\_url,
	));
	$auth\_result = curl\_exec($ch);
	$auth\_info = curl\_getinfo($ch);
	$auth\_url = $auth\_info['redirect\_url'];

	// Normally authentication will produce a 302 Redirect, but Xbox is special...
	if ($auth\_info['http\_code'] == 200) $auth\_url = $auth\_info['url'];

	curl\_close($ch);

	// No valid cookies
	if (strpos($auth\_url, $url.'?code') !== 0) {
		$result = false;
		switch($method) {
			case 'psn':
				$login\_url = 'https://auth.api.sonyentertainmentnetwork.com/login.do';

				// Login to PSN
				$ch = curl\_init();
				curl\_setopt\_array($ch, $default\_options);
				curl\_setopt\_array($ch, array(
					CURLOPT\_URL => $login\_url,
					CURLOPT\_POST => 3,
					CURLOPT\_POSTFIELDS => http\_build\_query(array(
						'params' => 'cmVxdWVzdF9sb2NhbGU9ZW5fVVMmcmVxdWVzdF90aGVtZT1saXF1aWQ=', // without empty server result
						'j\_username' => $username,
						'j\_password' => $password,
						'rememberSignIn' => 1 // Remember signin
					)),
				));
				curl\_exec($ch);
				$redirect\_url = curl\_getinfo($ch)['redirect\_url'];
				curl\_close($ch);

				if (strpos($redirect\_url, 'authentication\_error') !== false) return false;

				// Authenticate with Bungie
				$ch = curl\_init();
				curl\_setopt\_array($ch, $default\_options);
				curl\_setopt\_array($ch, array(
					CURLOPT\_URL => $redirect\_url,
					CURLOPT\_FOLLOWLOCATION => true
				));
				curl\_exec($ch);
				$result = curl\_getinfo($ch);
				curl\_close($ch);
				break;
			case 'xbox':
				$login\_url = 'https://login.live.com/ppsecure/post.srf?'.substr($redirect\_url, strpos($redirect\_url, '?')+1);
				preg\_match('/id\="i0327" value\="(.\*?)"\//', $auth\_result, $ppft);

				if (count($ppft) == 2) {
					$ch = curl\_init();
					curl\_setopt\_array($ch, $default\_options);
					curl\_setopt\_array($ch, array(
						CURLOPT\_URL => $login\_url,
						CURLOPT\_POST => 3,
						CURLOPT\_POSTFIELDS => http\_build\_query(array(
							'login' => $username,
							'passwd' => $password,
							'KMSI' => 1, // Stay signed in
							'PPFT' => $ppft[1]
						)),
						CURLOPT\_FOLLOWLOCATION => true
					));
					$auth\_result = curl\_exec($ch);
					$auth\_url = curl\_getinfo($ch)['url'];
					curl\_close($ch);

					if (strpos($auth\_url, $url.'?code') === 0) {
						return true;
					}
				}
				return false;
				break;
		}
		$result\_url = $result['url'];
		if ($result['http\_code'] == 302) $result\_url = $result['redirect\_url'];

		// Account has not been registered with Bungie
		if (strpos($result\_url, '/Register') !== false) return false;

		// Login successful, "bungleatk" should be set
		// Facebook/PSN should return with ?code=
		// Xbox should have ?wa=wsignin1.0
		return strpos($result\_url, $url) === 0;
	}
	// Valid Third Party Cookies, re-authenticating Bungie Login
	$ch = curl\_init();
	curl\_setopt\_array($ch, $default\_options);
	curl\_setopt\_array($ch, array(
		CURLOPT\_URL => $auth\_url,
	));
	curl\_exec($ch);
	curl\_close($ch);
	return true;
}

```
by lowlines

Psn login flow changed:

### **Python**

#### **Live Authentication**

```
import requests
import re

exp\_urlpost = r'urlPost:\'(https://.\*?)\''
exp\_ppft = r'<input type="hidden" name="PPFT" id=".\*" value="(.\*?)"/>'

url\_signin = 'https://www.bungie.net/en/User/SignIn/Xuid?bru=%252f'

apikey = 'YOURAPIKEY'
liveuser = 'SELF@EXPLANATORY.ORG'
livepass = 'SELFEXPLANATORY'

s = requests.Session()

r = s.get(url\_signin)
url\_post = re.findall(exp\_urlpost, r.content.decode())[0]
ppft = re.findall(exp\_ppft, r.content.decode())[0]
payload = { 'login': liveuser, 'passwd': livepass, 'PPFT': ppft }
r = s.post(url\_post, data = payload)

apiheaders = {'X-API-Key': apikey, 'x-csrf': s.cookies.get\_dict()['bungled']}

r = s.get('https://www.bungie.net/Platform/Destiny/1/MyAccount/Vault/', headers=apiheaders)

```
by Unblessedpants

#### **PSN Auth with Python/Requests**

```
import getpass
import logging

from base64 import b64encode
from urlparse import urlparse

import httplib
httplib.HTTPConnection.debuglevel = 0

import requests
# https://urllib3.readthedocs.org/en/latest/security.html#insecurerequestwarning
requests.packages.urllib3.disable\_warnings()

API\_KEY = None  # Use your own API Key

def login(username, password):
    logger = logging.getLogger(\_\_name\_\_)

    BUNGIE\_SIGNIN\_URI = "https://www.bungie.net/en/User/SignIn/Psnid"
    PSN\_OAUTH\_URI = "https://auth.api.sonyentertainmentnetwork.com/login.do"

    logger.info("Logging in...")

    # Get JSESSIONID cookie.
    # We follow the redirection just in case the URI ever changes.
    get\_jessionid = requests.get(BUNGIE\_SIGNIN\_URI, allow\_redirects=True)
    jsessionid0 = get\_jessionid.history[1].cookies["JSESSIONID"]
    logger.debug("JSESSIONID: %s", jsessionid0)

    # The POST request will fail if the field `params` isn't present
    # in the body of the request.
    # The value is just the query string of the PSN login page
    # encoded in base64.
    params = urlparse(get\_jessionid.url).query
    logger.debug("params: %s", params)
    params64 = b64encode(params)
    logger.debug("params64: %s", params64)

    # Post credentials and pass the JSESSIONID cookie.
    # We get a new JSESSIONID cookie.
    # Note: It doesn't appear to matter what the value of `params` is, but
    # we'll pass in the appropriate value just to be safe.
    post = requests.post(
        PSN\_OAUTH\_URI,
        data={"j\_username": username, "j\_password": password, "params": params64},
        cookies={"JSESSIONID": jsessionid0},
        allow\_redirects=False
    )
    if "authentication\_error" in post.headers["location"]:
        logger.warning("Invalid credentials")
    jsessionid1 = post.cookies["JSESSIONID"]
    logger.debug("JSESSIONID: %s", jsessionid1)

    # Follow the redirect from the previous request passing in the new
    # JSESSIONID cookie. This gets us the x-np-grant-code to complete
    # the sign-in with Bungie.
    get\_grant\_code = requests.get(
        post.headers["location"],
        allow\_redirects=False,
        cookies={"JSESSIONID": jsessionid1}
    )
    grant\_code = get\_grant\_code.headers["x-np-grant-code"]
    logger.debug("x-np-grant-code: %s", grant\_code)

    # Finish our sign-in with Bungie using the grant code.
    auth\_cookies = requests.get(BUNGIE\_SIGNIN\_URI,
                                params={"code": grant\_code})

    # Create requests Session.
    session = requests.Session()

    # Save the cookies indicating we've signed in to our session
    session.headers["X-API-Key"] = API\_KEY
    session.headers["x-csrf"] = auth\_cookies.cookies["bungled"]
    session.cookies.update({
        "bungleatk": auth\_cookies.cookies["bungleatk"],
        "bungled": auth\_cookies.cookies["bungled"],
        "bungledid": auth\_cookies.cookies["bungledid"]
    })

if \_\_name\_\_ == '\_\_main\_\_':
    logging.basicConfig(level=logging.DEBUG)
    ul3\_logger = logging.getLogger("requests.packages.urllib3")
    ul3\_logger.setLevel(logging.WARN)

    username = None
    password = None

    while not username:
        username = raw\_input("Enter Username: ")
    while not password:
       password = getpass.getpass("Enter Password: ")

    login(username, password)

```
 by Quantum Ascend

## References

1. ↑ https://www.bungie.net/en/Clan/Post/39966/106896643/0/0
2. ↑ https://www.bungie.net/en/Clan/Post/39966/132682213/0/0
3. ↑ https://www.bungie.net/en/Clan/Post/39966/107282722/0/0
4. ↑ https://www.bungie.net/en/Clan/Post/39966/119730323/0/0
5. ↑ https://www.bungie.net/en/Clan/Post/39966/122394884/0/0
6. ↑ https://www.bungie.net/en/Clan/Post/39966/112644874/0/0