AzureFeeds - All your Azure feeds in one place.

Sponsors

Monday, September 06, 2010

Twitter OAuth in F#

by Luke Hoban [gmail] via LukeH's WebLog on 9/6/2010 12:30:37 AM

I have a few F# demos which use the Twitter APIs as simple examples of accessing online data interactively and working with it in F#.    Recently, Twitter moved to require OAuth for accessing Twitter APIs on behalf of a user.  Below is the F# code I wrote to integrate OAuth, which should work for any other F# Twitter scripts and apps.

OAuth Implementation
open System
open System.IO
open System.Net
open System.Security.Cryptography
open System.Text

// Twitter OAuth Constants
let consumerKey : string = failwith "Must provide the consumerKey for an app registered at http://dev.twitter.com/apps/new"
let consumerSecret : string = failwith "Must provide the consumerSecret for an app registered at http://dev.twitter.com/apps/new"
let requestTokenURI = "https://api.twitter.com/oauth/request_token"
let accessTokenURI = "https://api.twitter.com/oauth/access_token"
let authorizeURI = "https://api.twitter.com/oauth/authorize"


// Utilities
let unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
let urlEncode str = 
    String.init (String.length str) (fun i -> 
        let symbol = str.[i]
        if unreservedChars.IndexOf(symbol) = -1 then
            "%" + String.Format("{0:X2}", int symbol)
        else
            string symbol)


// Core Algorithms
let hmacsha1 signingKey str = 
    let converter = new HMACSHA1(Encoding.ASCII.GetBytes(signingKey : string))
    let inBytes = Encoding.ASCII.GetBytes(str : string)
    let outBytes = converter.ComputeHash(inBytes)
    Convert.ToBase64String(outBytes)

let compositeSigningKey consumerSecret tokenSecret = 
    urlEncode(consumerSecret) + "&" + urlEncode(tokenSecret)

let baseString httpMethod baseUri queryParameters = 
    httpMethod + "&" + 
    urlEncode(baseUri) + "&" +
    (queryParameters 
     |> Seq.sortBy (fun (k,v) -> k)
     |> Seq.map (fun (k,v) -> urlEncode(k)+"%3D"+urlEncode(v))
     |> String.concat "%26") 

let createAuthorizeHeader queryParameters = 
    let headerValue = 
        "OAuth " + 
        (queryParameters
         |> Seq.map (fun (k,v) -> urlEncode(k)+"\x3D\""+urlEncode(v)+"\"")
         |> String.concat ",")
    headerValue

let currentUnixTime() = floor (DateTime.UtcNow - DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds


/// Request a token from Twitter and return:
///  oauth_token, oauth_token_secret, oauth_callback_confirmed
let requestToken() = 
    let signingKey = compositeSigningKey consumerSecret ""

    let queryParameters = 
        ["oauth_callback", "oob";
         "oauth_consumer_key", consumerKey;
         "oauth_nonce", System.Guid.NewGuid().ToString().Substring(24);
         "oauth_signature_method", "HMAC-SHA1";
         "oauth_timestamp", currentUnixTime().ToString();
         "oauth_version", "1.0"]

    let signingString = baseString "POST" requestTokenURI queryParameters
    let oauth_signature = hmacsha1 signingKey signingString

    let realQueryParameters = ("oauth_signature", oauth_signature)::queryParameters

    let req = WebRequest.Create(requestTokenURI, Method="POST")
    let headerValue = createAuthorizeHeader realQueryParameters
    req.Headers.Add(HttpRequestHeader.Authorization, headerValue)
    
    let resp = req.GetResponse()
    let stream = resp.GetResponseStream()
    let txt = (new StreamReader(stream)).ReadToEnd()
    
    let parts = txt.Split('&')
    (parts.[0].Split('=').[1],
     parts.[1].Split('=').[1],
     parts.[2].Split('=').[1] = "true")

/// Get an access token from Twitter and returns:
///   oauth_token, oauth_token_secret
let accessToken token tokenSecret verifier =
    let signingKey = compositeSigningKey consumerSecret tokenSecret

    let queryParameters = 
        ["oauth_consumer_key", consumerKey;
         "oauth_nonce", System.Guid.NewGuid().ToString().Substring(24);
         "oauth_signature_method", "HMAC-SHA1";
         "oauth_token", token;
         "oauth_timestamp", currentUnixTime().ToString();
         "oauth_verifier", verifier;
         "oauth_version", "1.0"]

    let signingString = baseString "POST" accessTokenURI queryParameters
    let oauth_signature = hmacsha1 signingKey signingString
    
    let realQueryParameters = ("oauth_signature", oauth_signature)::queryParameters
    
    let req = WebRequest.Create(accessTokenURI, Method="POST")
    let headerValue = createAuthorizeHeader realQueryParameters
    req.Headers.Add(HttpRequestHeader.Authorization, headerValue)
    
    let resp = req.GetResponse()
    let stream = resp.GetResponseStream()
    let txt = (new StreamReader(stream)).ReadToEnd()
    
    let parts = txt.Split('&')
    (parts.[0].Split('=').[1],
     parts.[1].Split('=').[1])

/// Compute the 'Authorization' header for the given request data
let authHeaderAfterAuthenticated url httpMethod token tokenSecret queryParams = 
    let signingKey = compositeSigningKey consumerSecret tokenSecret

    let queryParameters = 
            ["oauth_consumer_key", consumerKey;
             "oauth_nonce", System.Guid.NewGuid().ToString().Substring(24);
             "oauth_signature_method", "HMAC-SHA1";
             "oauth_token", token;
             "oauth_timestamp", currentUnixTime().ToString();
             "oauth_version", "1.0"]

    let signingQueryParameters = 
        List.append queryParameters queryParams

    let signingString = baseString httpMethod url signingQueryParameters
    let oauth_signature = hmacsha1 signingKey signingString
    let realQueryParameters = ("oauth_signature", oauth_signature)::queryParameters
    let headerValue = createAuthorizeHeader realQueryParameters
    headerValue

/// Add an Authorization header to an existing WebRequest 
let addAuthHeaderForUser (webRequest : WebRequest) token tokenSecret queryParams = 
    let url = webRequest.RequestUri.ToString()
    let httpMethod = webRequest.Method
    let header = authHeaderAfterAuthenticated url httpMethod token tokenSecret queryParams
    webRequest.Headers.Add(HttpRequestHeader.Authorization, header)

type System.Net.WebRequest with
    /// Add an Authorization header to the WebRequest for the provided user authorization tokens and query parameters
    member this.AddOAuthHeader(userToken, userTokenSecret, queryParams) =
        addAuthHeaderForUser this userToken userTokenSecret queryParams




let testing() =   

    // Compute URL to send user to to allow our app to connect with their credentials,
    // then open the browser to have them accept
    let oauth_token'', oauth_token_secret'', oauth_callback_confirmed = requestToken()
    let url = authorizeURI + "?oauth_token=" + oauth_token''
    System.Diagnostics.Process.Start("iexplore.exe", url)
    
    // *******NOTE********:
    // Get the 7 digit number from the web page, pass it to the function below to get oauth_token
    // Sample result if things go okay:
    //    val oauth_token_secret' : string = "lbll17CpNUlSt0FbfMKyfrzBwyHUrRrY8Ge2rEhs"
    //    val oauth_token' : string = "17033946-8vqDO7foX0TUNpqNg9MyJpO3Qui3nunkZPixxLs"
    let oauth_token, oauth_token_secret = accessToken oauth_token'' oauth_token_secret'' ("1579754")


    // Test 1:
    let streamSampleUrl2 = "http://api.twitter.com/1/statuses/home_timeline.xml"
    let req = WebRequest.Create(streamSampleUrl2) 
    req.AddOAuthHeader(oauth_token, oauth_token_secret, [])
    let resp = req.GetResponse()
    let strm = resp.GetResponseStream()
    let text = (new StreamReader(strm)).ReadToEnd()
    text

    // Test 2:
    System.Net.ServicePointManager.Expect100Continue <- false
let statusUrl = "http://twitter.com/statuses/update.xml" let request = WebRequest.Create (statusUrl, Method="POST") let tweet = urlEncode("Hello!") request.AddOAuthHeader(oauth_token,oauth_token_secret,["status",tweet]) let bodyStream = request.GetRequestStream() let bodyWriter = new StreamWriter(bodyStream) bodyWriter.Write("status=" + tweet) bodyWriter.Close() let resp = request.GetResponse() let strm = resp.GetResponseStream() let text = (new StreamReader(strm)).ReadToEnd() text
email it!bookmark it!digg it!

Original Post: Twitter OAuth in F#

Legal Note

The content of the postings is owned by the respective author. AzureFeeds is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on AzureFeeds, please contact us. Designated trademarks and brands are the property of their respective owners. All rights reserved.

Advertise with us