DAmn/Authtokens

From Botdom Documentation
Jump to: navigation, search

deviantART uses authtokens to validate your login when you log into the website. This authtoken is required for you to be able to interact with the website as a logged in user, and as such must be provided in order to log into the dAmn Server. Authtokens used to be given as part of a cookie called userinfo which you receive when you log in, but this was changed February 2011 and it is now a separate cookie altogether called auth. Examples for fetching the authtoken under the old system can be found here.

Bots

Older bots such as dAmnBot required you to manually find and enter your bot's authtoken. This usually involves searching through the source code of the page at http://chat.deviantart.com/chat/channel, where channel is the name of the channel you want to join. These days, as a part of making things easier to use, most bots have a class or function dedicated to fetching your bot's authtoken for you. People tend to refer to these functions or classes as the "authtoken grabber", as it grabs the authtoken from a header returned by deviantART.com. Some grabbers return the whole cookie given by deviantART.com, and some only give the authtoken itself.

Grabbers

Here, you can see examples of authtoken grabbers written in different languages.

C#

dAmnStorm

This is a very poorly done authtoken grabber for the latest change in the deviantart login system. <csharp> using System; using System.Net; using System.Web; using System.IO;

namespace dAmnStorm {

   public class Login
   {
       public String GrabAuth(String user, String pass)
       {
           CookieContainer cj = new CookieContainer();
           String post = String.Format("&username={0}&password={1}&remember_me=1", user, pass);
           HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://www.deviantart.com/users/login");
           request.Host = "www.deviantart.com";
           request.UserAgent = "dAmnStorm";
           request.Referer = "https://www.deviantart.com/users/rockedout";
           request.KeepAlive = false;
           request.CookieContainer = cj;
           request.Accept = "text/html";
           request.ContentType = "application/x-www-form-urlencoded";
           request.ContentLength = post.Length;
           request.Method = "POST";
           StreamWriter writer = new StreamWriter(request.GetRequestStream());
           writer.Write(post);
           writer.Flush();
           HttpWebResponse response = (HttpWebResponse)request.GetResponse();
           writer.Close();
           HttpWebRequest request2 = (HttpWebRequest)WebRequest.Create("http://chat.deviantart.com/chat/Botdom");
           request2.Host = "chat.deviantart.com";
           request2.UserAgent = "dAmnStorm";
           request2.Referer = "http://chat.deviantart.com";
           request2.CookieContainer = cj;
           request2.KeepAlive = false;
           request2.Accept = "text/html";
           request2.ContentType = "application/x-www-form-urlencoded";
           request2.Method = "GET";
           HttpWebResponse response2 = (HttpWebResponse)request2.GetResponse();
           StreamReader read = new StreamReader(response2.GetResponseStream());
           string webpage = read.ReadToEnd();
           read.Close();
           response.Close();
           response2.Close();
           if (webpage.Contains("dAmn_Login("))
           {
               string auth = webpage.Substring(webpage.IndexOf("dAmn_Login(") + ((13 + user.Length) + 4), 32);
               Console.WriteLine("\n\n{0}", auth);
           }
           else
           {
               Console.WriteLine("ERROR: auth token not found on page");
           }
       }
   }

}

</csharp>

dAmnStorm update by plaguethenet

This is a minor modification to the dAmnStorm grabber, It cleans up the IndexOf use with regular expressions, and secures the post request against a malicious user and/or special characters in the password. This is the production code now used for cheddar, Thank you to whoever wrote dAmnStorm.

<csharp>

               public static string GetToken(string username, string password)

{ CookieContainer cj = new CookieContainer();

           String post = String.Format("&username={0}&password={1}&remember_me=1", HttpUtility.UrlEncode(username), HttpUtility.UrlEncode(password));
           HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://www.deviantart.com/users/login");
           request.UserAgent = "dAmnStorm";
           request.Referer = "https://www.deviantart.com/users/rockedout";
           request.KeepAlive = false;
           request.CookieContainer = cj;
           request.Accept = "text/html";
           request.ContentType = "application/x-www-form-urlencoded";
           request.ContentLength = post.Length;
           request.Method = "POST";
           StreamWriter writer = new StreamWriter(request.GetRequestStream());
           writer.Write(post);
           writer.Flush();
           HttpWebResponse response = (HttpWebResponse)request.GetResponse();
           writer.Close();
           HttpWebRequest request2 = (HttpWebRequest)WebRequest.Create("http://chat.deviantart.com/chat/Botdom");
           request2.UserAgent = "dAmnStorm";
           request2.Referer = "http://chat.deviantart.com";
           request2.CookieContainer = cj;
           request2.KeepAlive = false;
           request2.Accept = "text/html";
           request2.ContentType = "application/x-www-form-urlencoded";
           request2.Method = "GET";
           HttpWebResponse response2 = (HttpWebResponse)request2.GetResponse();
           StreamReader read = new StreamReader(response2.GetResponseStream());
           string webpage = read.ReadToEnd();
           read.Close();
           response.Close();
           response2.Close();

string pattern = "dAmn_Login\\( \"(.*?)\", \"(.*?)\" \\);"; if(Regex.IsMatch(webpage, pattern)) { Match m = Regex.Match(webpage, pattern); string ret = Regex.Replace(m.Value, pattern, "$2"); return ret; } else { throw new OperationCanceledException("Unable to extract auth token."); } } </csharp>

PHP

dAmnPHP

This is adapted from a method from the main class in dAmnPHP.

<php>

       function getCookie($username, $pass) {
               // Method to get the cookie! Yeah! :D
               // Our first job is to open an SSL connection with our host.
               $socket = fsockopen(
                       $this->server['login']['transport'].$this->server['login']['host'],
                       $this->server['login']['port']
               );
               // If we didn't manage that, we need to exit!
               if($socket === false) {
               return array(
                       'status' => 2,
                       'error' => 'Could not open an internet connection');
               }
               // Fill up the form payload
               $POST = '&username='.urlencode($username);
               $POST.= '&password='.urlencode($pass);
               $POST.= '&remember_me=1';
               // And now we send our header and post data and retrieve the response.
               $response = $this->send_headers(
                   $socket,
                   $this->server['login']['host'],
                   $this->server['login']['file'],
                   "http://www.deviantart.com/users/rockedout",
                   $POST
               );
              
               // Now that we have our data, we can close the socket.
               fclose ($socket);
               // And now we do the normal stuff, like checking if the response was empty or not.
               if(empty($response))
               return array(
                       'status' => 3,
                       'error' => 'No response returned from the server'
               );
               if(stripos($response, 'set-cookie') === false)
               return array(
                       'status' => 4,
                       'error' => 'No cookie returned'
                   );
               // Grab the cookies from the header
               $response=explode("\r\n", $response);
               $cookie_jar = array();
               foreach ($response as $line)
                   if (strpos($line, "Set-Cookie:")!== false)
                       $cookie_jar[] = substr($line, 12, strpos($line, "; ")-12);
               // Using these cookies, we're gonna go to chat.deviantart.com and get
               // our authtoken from the dAmn client.
               if (($socket = @fsockopen("ssl://www.deviantart.com", 443)) == false)
                    return array(
                       'status' => 2,
                       'error' => 'Could not open an internet connection');
               $response = $this->send_headers(
                   $socket,
                   "chat.deviantart.com",
                   "/chat/Botdom",
                   "http://chat.deviantart.com",
                   null,
                   $cookie_jar
               );
               // Now search for the authtoken in the response
               $cookie = null;
               if (($pos = strpos($response, "dAmn_Login( ")) !== false)
               {
                   $response = substr($response, $pos+12);
                   $cookie = substr($response, strpos($response, "\", ")+4, 32);
               }
               else return array(
                   'status' => 4,
                   'error' => 'No authtoken found in dAmn client'
               );
                                 
               // Because errors still happen, we need to make sure we now have an array!
               if(!$cookie)
               return array(
                       'status' => 5,
                       'error' => 'Malformed cookie returned'
               );
               // We got a valid cookie!
               return array(
                       'status' => 1,
                       'cookie' => $cookie
               );
       }
       
       function send_headers($socket, $host, $url, $referer, $post=null, $cookies=array())
       {
           try
           {
               $headers = "";
               if (isset($post))
                   $headers .= "POST $url HTTP/1.1\r\n";
               else $headers .= "GET $url HTTP/1.1\r\n";
               $headers .= "Host: $host\r\n";
               $headers .= "User-Agent: ".$this->Agent."\r\n";
               $headers .= "Referer: $referer\r\n";
               if ($cookies != array())
                   $headers .= "Cookie: ".implode("; ", $cookies)."\r\n";
               $headers .= "Connection: close\r\n";
               $headers .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*\/*;q=0.8\r\n";
               $headers .= "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n";
               $headers .= "Content-Type: application/x-www-form-urlencoded\r\n";
               if (isset($post))
                   $headers .= "Content-Length: ".strlen($post)."\r\n\r\n$post";
               else $headers .= "\r\n";
               $response = "";
               fputs($socket, $headers);
               while (!@feof ($socket)) $response .= @fgets ($socket, 8192);
               return $response;
           }
           catch (Exception $e)
           {
               echo "Exception occured: ".$e->getMessage()."\n";
               return "";
           }
       }

</php>

deviant-garde's token grabber

deviant-garde made this one as part of a command-line script separate from a bot.

<php> function getAuthtoken($username, $password) // grab the bot's authtoken {

   // first, get the cookies from logging in
   $post = "username=$username&password=$password&remember_me=1";
   if (($socket = @fsockopen("ssl://www.deviantart.com", 443)) == false)
       die("Failed to connect to deviantART to get cookies.\n");
   $cookies = array();
   $response = send_headers(
                 $socket,
                 "www.deviantart.com",
                 "/users/login",
                 "http://www.deviantart.com/users/rockedout",
                 $post
   );
   fclose($socket);
   $response=explode("\r\n", $response);
   foreach ($response as $line)
       if (strpos($line, "Set-Cookie:")!== false)
           $cookies[] = substr($line, 12, strpos($line, "; ")-12);
   if ($cookies==array())
       die("Couldn't retrieve the authtoken. Please check your username and password.\n");
   // now grab the authtoken from the dAmn client
   if (($socket = @fsockopen("chat.deviantart.com", 80)) == false)
       die("Failed to connect to deviantART to get authtoken.\n");
   $response = send_headers(
                 $socket,
                 "chat.deviantart.com",
                 "/chat/Botdom",
                 "http://chat.deviantart.com",
                 null,
                 $cookies
   );
   if (($pos = strpos($response, "dAmn_Login( ")) !== false)
   {
       $response = substr($response, $pos+12);
       return substr($response, strpos($response, "\", ")+4, 32);
   }
   else
       die("Could not find authtoken!\nRESPONSE: $response\n");

}

function send_headers($socket, $host, $url, $referer, $post=null, $cookies=array()) {

   try
   {
       $headers = "";
       if (isset($post))
           $headers .= "POST $url HTTP/1.1\r\n";
       else $headers .= "GET $url HTTP/1.1\r\n";
       $headers .= "Host: $host\r\n";
       $headers .= "User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.8) Gecko/20100731 Firefox/3.6.8 (Swiftfox)\r\n";
       $headers .= "Referer: $referer\r\n";
       $headers .= "Connection: close\r\n";
       if ($cookies != array())
           $headers .= "Cookie: ".implode("; ", $cookies)."\r\n";
       $headers .= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*\/*;q=0.8\r\n";
       $headers .= "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n";
       $headers .= "Content-Type: application/x-www-form-urlencoded\r\n";
       if (isset($post))
           $headers .= "Content-Length: ".strlen($post)."\r\n\r\n$post";
       else $headers .= "\r\n";
       $response = "";
       fputs($socket, $headers);
       while (!feof ($socket)) $response .= fgets ($socket, 8192);
       return $response;
   }
   catch (Exception $e)
   {
       echo "Exception occured: ".$e->getMessage()."\n";
       return "";
   }

} </php>

Haskell

Arsenic

This is used in a Haskell bot being developed by deviant-garde, which currently does not have a wiki page.

<haskell> import qualified Data.ByteString.Lazy.Char8 as L import Data.List (find) import Network.Curl import Network.Curl.Easy

-- | Log in on deviantART and return the recieved authtoken using cURL. getAuthToken :: ByteString -> ByteString -> Bool -> IO (Maybe ByteString) getAuthToken username pass reusetoken = withCurlDo $

   do context <- initialize
      setopt context $ CurlPostFields [ "username="++(L.unpack username)
                                      , "password="++(L.unpack pass)
                                      , "reusetoken="++if reusetoken
                                                          then "1"
                                                          else "0" ]
      setopt context $ CurlURL "https://www.deviantart.com/users/login"
      response <- perform_with_response_ context
               :: IO CurlResponse
      let cookies = map (fst . break (';'==) . drop 1 . snd)
                  . filter (("Set-Cookie"==) . fst)
                  $ respHeaders response
      reset context
      setopt context . CurlHttpHeaders $ ["Cookie: " ++ intercalate "; " cookies]
      setopt context $ CurlURL "http://chat.deviantart.com/chat/Botdom"
      response' <- perform_with_response_ context
                :: IO CurlResponse
      case subIndex "dAmn_Login( " $ respBody response' of
        Nothing -> return Nothing
        Just pos -> let body = drop (pos+12) $ respBody response'
                    in case subIndex "\", " body of
                         Nothing -> return Nothing
                         Just pos' -> return . Just . L.pack . take 32
                                    $ drop (pos'+4) body

</haskell>

Python

dAmn Viper

The authtoken grabber in dAmn Viper involves more than one file, to achieve compatibility with multiple versions of Python. Terra, however, is built for Python3.1, so the code listed here is only compatible with Python 3. So, the first module, which should be used by other applications if the authtoken is required, is <python-inline>dAmnViper.deviantART</python-inline>, and the code we're interested in from this module is shown below. <python> dAmn Viper - A Python API for dAmn.

   Copyright (C) 2009  Henry Rapley <froggywillneverdie@msn.com>
   Released under a GNU GPL.

import os import re

_pyver = float(os.sys.version[:3]) if _pyver >= 2.6 and _pyver < 3:

   from dAmnViper._auth26 import fetch_cookie
   from dAmnViper._auth26 import fetch_channel

elif _pyver >= 3:

   from dAmnViper._auth3 import fetch_cookie
   from dAmnViper._auth3 import fetch_channel

else:

   import sys
   sys.stdout.write('>>> Your Python install must be at least Python 2.6!\n')
   sys.stdout.write('>>> Python 3.1 is preferable.\n')
   sys.stdout.flush()
   sys.exit(1)


class Login:

   """This class uses given login data to fetch a deviantART cookie and authtoken."""
   
   url = 'https://www.deviantart.com/users/login'
   curl = 'http://chat.deviantart.com/chat/botdom'
   
   def __init__(self, username, passwrd, extras={'reusetoken':'1'}, client='dAmnViper (python 3.x) TokenGrabber/2'):
       """ Initialise the object. Fetch and process an authtoken. """
       self.jar = None
       self.cookie = None
       self.token = None
       self.response = None
       self.status = (0, 'Nothing has happened yet.')
       # Attempt to fetch the authoken!
       response = fetch_cookie(self, username, password, extras, client)
       # Process the response!
       url = response.geturl()
       if url == 'ConnectionError' or not 'loggedin=1' in url:
           self.handle(response)
           return
       response = fetch_channel(self, self.curl, client)
       # Process the response!
       url = response.geturl()
       if url == 'ConnectionError' or url != self.curl:
           self.handle(response)
           return
       self.crop(username, response.data)
   
   def crop(self, username, data):
       match = re.search('"'+username+'", "([0-9a-f]{32})"', data)
       if match is None or match.group(1) is None:
           self.status = (2, 'Authtoken not given. Not sure why.')
           return
       self.token = match.group(1)
       self.status = (1, 'Authtoken retrieved!')
   
   def handle(self, response):
       """ Handle a login failure. """
       loc = response.geturl()
       if 'wrong-password' in loc:
           self.status = (4, 'Incorrect username or password provided.')
           return
       if loc == 'localhost':
           status = response.headers.get('Status')
           if status[0] == -2:
               self.status = (3, 'Could not connect to the internet.')
           else:
               self.status = (5, status[1])
           return
       self.status = (6, 'Something went wrong. I do not know why.')

</python> This works in conjunction with the following code from <python-inline>dAmnViper._auth3</python-inline>: <python> import re import urllib.parse import urllib.request import http.cookiejar

class HTTPResponder:

   headers = {
       'Location': 'localhost',
       'Status': (0, False),
   }
   
   def __init__(self, url, data=):
       self.url = url
       self.data = data
   
   def geturl(self, *args, **kwargs):
       return self.url
   

def fetch_cookie(obj, username, password, extras={'reusetoken':'1'}, client='dAmnViper (python3.x) TokenGrabber/2'):

   extras.update({'username': username, 'password': password})
   obj.jar = http.cookiejar.CookieJar()
   opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(obj.jar))
   req = urllib.request.Request(
       obj.url,
       urllib.parse.urlencode(extras),
       {'User-Agent': client},
   )
   try:
       response = opener.open(req)
   except IOError as e:
       response = HTTPResponder('ConnectionError')
       response.headers['Status'] = (e.reason.errno, e.strerror)
   return response
   # Well, that was nice and easy :D

def fetch_channel(obj, url='http://chat.deviantart.com/chat/botdom', client='dAmnViper (python3.x) TokenGrabber/2'):

   opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(obj.jar))
   req = urllib.request.Request(
       url,
       urllib.parse.urlencode({}),
       {'User-Agent': client})
   response = {}
   try:
       resp = opener.open(req)
       response = HTTPResponder(resp.geturl(), resp.read().decode('latin-1'))
   except IOError as e:
       response = HTTPResponder('ConnectionError')
       response.headers['Status'] = (e.reason.errno, e.strerror)
   return response

</python> Example usage is below: <python> from dAmnViper.deviantART import Login

session = Login('username', 'password') print(session.status[1]) # Tells us what happened! print(session.token) # Shows the authtoken, if there is one. </python>

Java

Fwertz's ugly Java rendition

    ====Updated 12/22/2011=====

<java> import java.io.*; import java.net.*; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.HttpsURLConnection;

private static final String _ENCODE = "UTF-8"; private static final boolean _PERSIST = true; private static final String _LOGINURL = "https://www.deviantart.com/users/login"; private static final String _TOKENURL = "http://chat.deviantart.com/chat/botdom"; private static final String _TOKENPATTERN = "(?<=\",\\s\")(?!\"\\s\\);)[a-zA-Z0-9]{32}";

public static String getAuthToken(String user, String password) {

   String requestString, userEncode, passEncode, cookies, page, token;
   ArrayList<String[]> list;
   HttpsURLConnection conn;
   HttpURLConnection conn2;
   DataOutputStream out;
   BufferedReader in;
   Pattern pattern;
   Matcher match;
   URL url;
   /* Encode request string variables */
   try {
       userEncode = URLEncoder.encode(user, _ENCODE);
       passEncode = URLEncoder.encode(password, _ENCODE);
   } catch (UnsupportedEncodingException e) {
       System.err.println("Encode type: "+ _ENCODE + " not supported.");
       if(!_PERSIST) return null;
       System.out.println("Using deprecated Encode...");
       userEncode = URLEncoder.encode(user);
       passEncode = URLEncoder.encode(password);
   }
       
   /* Build request String */
   requestString = "username=" + userEncode + "&password=" + passEncode
                   + "&loggedin=1";
       
   /* Build first connection */
   try {
        url = new URL(_LOGINURL);
        conn = (HttpsURLConnection) url.openConnection();
   } catch (MalformedURLException e) {
       System.err.println("URL:" + _LOGINURL + " malformed or invalid.");
       return null;
   } catch (IOException e) {
       System.err.println("Cannot open URL:" + _LOGINURL);
       return null;
   }
       
   /* Build first web request */
   if(conn!=null) {	
       try {
           conn.setRequestProperty("User-Agent", "Mozilla 4.0"); /* Lazy UA */
           conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
           conn.setRequestProperty("Content-Length", ""+requestString.getBytes().length);
           conn.setRequestMethod("POST");
           conn.setDoOutput(true);
               
           out = new DataOutputStream(conn.getOutputStream());		/* Yes, this blocks, relax */
           out.writeBytes(requestString);
           out.close();	
       } catch(ProtocolException e) {
           System.err.println("Protocol error for connection using method: " + conn.getRequestMethod());
           return null;
       } catch(IOException e) {
           System.err.println("Error in connecting/writing to: "  + conn.getURL());
           return null;
       }
       conn.disconnect();
   }
       
   /* Read in the headers from connection */
   list = new ArrayList<String[]>();
   for(int i = 0; ; i++) {
       String[] vals;
       String headerKey = conn.getHeaderFieldKey(i);
       if(conn.getHeaderField(i) == null)
           break;
       if("Set-Cookie".equalsIgnoreCase(headerKey)) {
           vals = conn.getHeaderField(i).split(";\\s*");
           list.add(vals);
       }
   }
       
   /* Build cookie header string */
   cookies = "";
   for(int i=0; i < list.size(); i++)
       for(int n=0; n<list.get(i).length; n++)
           cookies+= list.get(i)[n]+";";
       
   /* Build second connection */
   try {
       url = new URL(_TOKENURL);
       conn2= (HttpURLConnection) url.openConnection();
   } catch (MalformedURLException e){
       System.err.println("URL:" + _TOKENURL + " malformed or invalid.");
       return null;
   } catch (IOException e) {
       System.err.println("Cannot open URL:" + _TOKENURL);
       return null;
   }
       
   /* Build second web request */
   page = "";
   if(conn2!=null) {
       String line;
       try {
           conn2.setRequestProperty("User-Agent", conn.getRequestProperty("User-Agent"));
           conn2.setRequestProperty("Content-Type", conn.getRequestProperty("Content-Type"));
           conn2.setRequestMethod("GET");
           conn2.setRequestProperty("Cookie", cookies);
                   
           /* Read in the contents of the page */
           in = new BufferedReader(new InputStreamReader(conn2.getInputStream()));
           while((line = in.readLine())!= null)	/* I know, blocking, I know */
               page += line;
           in.close();
       } catch(ProtocolException e) {
           System.err.println("Protocol error for connection using method: " + conn2.getRequestMethod());
           return null;
       } catch(IOException e) {
           System.err.println("Error in connecting/reading to/from: "  + conn2.getURL());
           return null;
       }
       conn2.disconnect();
   }
       
   /* Brute out the token from the page contents */
   token = "";
   pattern = Pattern.compile(_TOKENPATTERN);
   match = pattern.matcher(page);
   while(match.find())
       token = match.group();
   /* Yay. */
   return token;

} </java>