PureBasic
PureBasic
Etsy OAuth1 Authorization
See more Etsy Examples
Demonstrates 3-legged OAuth1 authorization for Etsy.Chilkat PureBasic Downloads
IncludeFile "CkJsonObject.pb"
IncludeFile "CkOAuth2.pb"
IncludeFile "CkHttp.pb"
IncludeFile "CkFileAccess.pb"
IncludeFile "CkSocket.pb"
IncludeFile "CkHashtable.pb"
IncludeFile "CkHttpRequest.pb"
IncludeFile "CkHttpResponse.pb"
IncludeFile "CkTask.pb"
IncludeFile "CkStringBuilder.pb"
Procedure ChilkatExample()
success.i = 0
consumerKey.s = "keystring"
consumerSecret.s = "shared_secret"
; Specify one or more SPACE separated scopes as query params in the requestTokenUrl
; See https://www.etsy.com/developers/documentation/getting_started/oauth#section_permission_scopes
requestTokenUrl.s = "https://openapi.etsy.com/v2/oauth/request_token?scope=email_r%20listings_r%20listings_w%20listings_d"
authorizeUrl.s = "https://www.etsy.com/oauth/signin"
accessTokenUrl.s = "https://openapi.etsy.com/v2/oauth/access_token"
; The port number is picked at random. It's some unused port that won't likely conflict with anything else..
callbackUrl.s = "http://localhost:3017/"
callbackLocalPort.i = 3017
; The 1st step in 3-legged OAuth1.0a is to send a POST to the request token URL to obtain an OAuth Request Token
http.i = CkHttp::ckCreate()
If http.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkHttp::setCkOAuth1(http, 1)
CkHttp::setCkOAuthConsumerKey(http, consumerKey)
CkHttp::setCkOAuthConsumerSecret(http, consumerSecret)
CkHttp::setCkOAuthCallback(http, callbackUrl)
req.i = CkHttpRequest::ckCreate()
If req.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkHttpRequest::setCkHttpVerb(req, "POST")
CkHttpRequest::setCkContentType(req, "application/x-www-form-urlencoded")
resp.i = CkHttpResponse::ckCreate()
If resp.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
success = CkHttp::ckHttpReq(http,requestTokenUrl,req,resp)
If success = 0
Debug CkHttp::ckLastErrorText(http)
CkHttp::ckDispose(http)
CkHttpRequest::ckDispose(req)
CkHttpResponse::ckDispose(resp)
ProcedureReturn
EndIf
; If successful, the resp.BodyStr contains something like this:
; login_url=https%3A%2F%2Fwww.etsy.com%2Foauth%2Fsignin%3Foauth_consumer_key%3D9ad9l1omxzbwfr2niq0ce1ly%26oauth_token%3D7116b4d0c72c2736561853d9e50113%26service%3Dv2_prod&oauth_token=7116b4d0c72c2736561853d9e50113&oauth_token_secret=3b7612b5d3&oauth_callback_confirmed=true&oauth_consumer_key=9ad9l1omxzbwfr2niq0ce1ly&oauth_callback=http%3A%2F%2Flocalhost%3A3017%2F
Debug CkHttpResponse::ckBodyStr(resp)
; We'll need this for later..
hashTab.i = CkHashtable::ckCreate()
If hashTab.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkHashtable::ckAddQueryParams(hashTab,CkHttpResponse::ckBodyStr(resp))
requestToken.s = CkHashtable::ckLookupStr(hashTab,"oauth_token")
requestTokenSecret.s = CkHashtable::ckLookupStr(hashTab,"oauth_token_secret")
CkHttp::setCkOAuthTokenSecret(http, requestTokenSecret)
Debug "oauth_token = " + requestToken
Debug "oauth_token_secret = " + requestTokenSecret
; ---------------------------------------------------------------------------
; The next step is to form a URL to send to the authorizeUrl
; This is an HTTP GET that we load into a popup browser.
sbUrlForBrowser.i = CkStringBuilder::ckCreate()
If sbUrlForBrowser.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckAppend(sbUrlForBrowser,authorizeUrl)
CkStringBuilder::ckAppend(sbUrlForBrowser,"?")
CkStringBuilder::ckAppend(sbUrlForBrowser,CkHttpResponse::ckBodyStr(resp))
url.s = CkStringBuilder::ckGetAsString(sbUrlForBrowser)
; When the url is loaded into a browser, the response from Etsy will redirect back to localhost:3017
; We'll need to start a socket that is listening on port 3017 for the callback from the browser.
listenSock.i = CkSocket::ckCreate()
If listenSock.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
backLog.i = 5
success = CkSocket::ckBindAndListen(listenSock,callbackLocalPort,backLog)
If success = 0
Debug CkSocket::ckLastErrorText(listenSock)
CkHttp::ckDispose(http)
CkHttpRequest::ckDispose(req)
CkHttpResponse::ckDispose(resp)
CkHashtable::ckDispose(hashTab)
CkStringBuilder::ckDispose(sbUrlForBrowser)
CkSocket::ckDispose(listenSock)
ProcedureReturn
EndIf
; Wait for the browser's connection in a background thread.
; (We'll send load the URL into the browser following this..)
; Wait a max of 60 seconds before giving up.
sock.i = CkSocket::ckCreate()
If sock.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
maxWaitMs.i = 60000
task.i = CkSocket::ckAcceptNextAsync(listenSock,maxWaitMs,sock)
CkTask::ckRun(task)
; Launch the system's default browser navigated to the URL.
oauth2.i = CkOAuth2::ckCreate()
If oauth2.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
success = CkOAuth2::ckLaunchBrowser(oauth2,url)
If success = 0
Debug CkOAuth2::ckLastErrorText(oauth2)
CkHttp::ckDispose(http)
CkHttpRequest::ckDispose(req)
CkHttpResponse::ckDispose(resp)
CkHashtable::ckDispose(hashTab)
CkStringBuilder::ckDispose(sbUrlForBrowser)
CkSocket::ckDispose(listenSock)
CkSocket::ckDispose(sock)
CkOAuth2::ckDispose(oauth2)
ProcedureReturn
EndIf
; Wait for the listenSock's task to complete.
success = CkTask::ckWait(task,maxWaitMs)
If Not success OR (CkTask::ckStatusInt(task) <> 7) OR (CkTask::ckTaskSuccess(task) <> 1)
If Not success
; The task.LastErrorText applies to the Wait method call.
Debug CkTask::ckLastErrorText(task)
Else
; The ResultErrorText applies to the underlying task method call (i.e. the AcceptNextConnection)
Debug CkTask::ckStatus(task)
Debug CkTask::ckResultErrorText(task)
EndIf
CkTask::ckDispose(task)
CkHttp::ckDispose(http)
CkHttpRequest::ckDispose(req)
CkHttpResponse::ckDispose(resp)
CkHashtable::ckDispose(hashTab)
CkStringBuilder::ckDispose(sbUrlForBrowser)
CkSocket::ckDispose(listenSock)
CkSocket::ckDispose(sock)
CkOAuth2::ckDispose(oauth2)
ProcedureReturn
EndIf
; If we get to this point, the connection from the browser arrived and was accepted.
; We no longer need the listen socket...
; Stop listening on port 3017.
CkSocket::ckClose(listenSock,10)
CkTask::ckDispose(task)
; Read the start line of the request..
startLine.s = CkSocket::ckReceiveUntilMatch(sock,Chr(13) + Chr(10))
If CkSocket::ckLastMethodSuccess(sock) = 0
Debug CkSocket::ckLastErrorText(sock)
CkHttp::ckDispose(http)
CkHttpRequest::ckDispose(req)
CkHttpResponse::ckDispose(resp)
CkHashtable::ckDispose(hashTab)
CkStringBuilder::ckDispose(sbUrlForBrowser)
CkSocket::ckDispose(listenSock)
CkSocket::ckDispose(sock)
CkOAuth2::ckDispose(oauth2)
ProcedureReturn
EndIf
; Read the request header.
requestHeader.s = CkSocket::ckReceiveUntilMatch(sock,Chr(13) + Chr(10) + Chr(13) + Chr(10))
If CkSocket::ckLastMethodSuccess(sock) = 0
Debug CkSocket::ckLastErrorText(sock)
CkHttp::ckDispose(http)
CkHttpRequest::ckDispose(req)
CkHttpResponse::ckDispose(resp)
CkHashtable::ckDispose(hashTab)
CkStringBuilder::ckDispose(sbUrlForBrowser)
CkSocket::ckDispose(listenSock)
CkSocket::ckDispose(sock)
CkOAuth2::ckDispose(oauth2)
ProcedureReturn
EndIf
; The browser SHOULD be sending us a GET request, and therefore there is no body to the request.
; Once the request header is received, we have all of it.
; We can now send our HTTP response.
sbResponseHtml.i = CkStringBuilder::ckCreate()
If sbResponseHtml.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckAppend(sbResponseHtml,"<html><body><p>Chilkat thanks you!</b></body</html>")
sbResponse.i = CkStringBuilder::ckCreate()
If sbResponse.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckAppend(sbResponse,"HTTP/1.1 200 OK" + Chr(13) + Chr(10))
CkStringBuilder::ckAppend(sbResponse,"Content-Length: ")
CkStringBuilder::ckAppendInt(sbResponse,CkStringBuilder::ckLength(sbResponseHtml))
CkStringBuilder::ckAppend(sbResponse,Chr(13) + Chr(10))
CkStringBuilder::ckAppend(sbResponse,"Content-Type: text/html" + Chr(13) + Chr(10))
CkStringBuilder::ckAppend(sbResponse,Chr(13) + Chr(10))
CkStringBuilder::ckAppendSb(sbResponse,sbResponseHtml)
CkSocket::ckSendString(sock,CkStringBuilder::ckGetAsString(sbResponse))
CkSocket::ckClose(sock,50)
; The information we need is in the startLine.
; For example, the startLine will look like this:
; GET /?oauth_token=a3bc8bec84acc31418b68a532e9511&oauth_verifier=b5558d37 HTTP/1.1
sbStartLine.i = CkStringBuilder::ckCreate()
If sbStartLine.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkStringBuilder::ckAppend(sbStartLine,startLine)
numReplacements.i = CkStringBuilder::ckReplace(sbStartLine,"GET /?","")
numReplacements = CkStringBuilder::ckReplace(sbStartLine," HTTP/1.1","")
CkStringBuilder::ckTrim(sbStartLine)
; oauth_token=a3bc8bec84acc31418b68a532e9511&oauth_verifier=b5558d37
Debug "startline: " + CkStringBuilder::ckGetAsString(sbStartLine)
CkHashtable::ckClear(hashTab)
CkHashtable::ckAddQueryParams(hashTab,CkStringBuilder::ckGetAsString(sbStartLine))
requestToken = CkHashtable::ckLookupStr(hashTab,"oauth_token")
authVerifier.s = CkHashtable::ckLookupStr(hashTab,"oauth_verifier")
; ------------------------------------------------------------------------------
; Finally , we must exchange the OAuth Request Token for an OAuth Access Token.
CkHttp::setCkOAuthToken(http, requestToken)
CkHttp::setCkOAuthVerifier(http, authVerifier)
CkHttpRequest::setCkHttpVerb(req, "POST")
CkHttpRequest::setCkContentType(req, "application/x-www-form-urlencoded")
success = CkHttp::ckHttpReq(http,accessTokenUrl,req,resp)
If success = 0
Debug CkHttp::ckLastErrorText(http)
CkHttp::ckDispose(http)
CkHttpRequest::ckDispose(req)
CkHttpResponse::ckDispose(resp)
CkHashtable::ckDispose(hashTab)
CkStringBuilder::ckDispose(sbUrlForBrowser)
CkSocket::ckDispose(listenSock)
CkSocket::ckDispose(sock)
CkOAuth2::ckDispose(oauth2)
CkStringBuilder::ckDispose(sbResponseHtml)
CkStringBuilder::ckDispose(sbResponse)
CkStringBuilder::ckDispose(sbStartLine)
ProcedureReturn
EndIf
; Make sure a successful response was received.
If CkHttpResponse::ckStatusCode(resp) <> 200
Debug CkHttpResponse::ckStatusLine(resp)
Debug CkHttpResponse::ckHeader(resp)
Debug CkHttpResponse::ckBodyStr(resp)
CkHttp::ckDispose(http)
CkHttpRequest::ckDispose(req)
CkHttpResponse::ckDispose(resp)
CkHashtable::ckDispose(hashTab)
CkStringBuilder::ckDispose(sbUrlForBrowser)
CkSocket::ckDispose(listenSock)
CkSocket::ckDispose(sock)
CkOAuth2::ckDispose(oauth2)
CkStringBuilder::ckDispose(sbResponseHtml)
CkStringBuilder::ckDispose(sbResponse)
CkStringBuilder::ckDispose(sbStartLine)
ProcedureReturn
EndIf
; If successful, the resp.BodyStr contains something like this:
; oauth_token=7898d7ba280dc791586dcfd26b37a9&oauth_token_secret=f2a7c267aa
Debug CkHttpResponse::ckBodyStr(resp)
CkHashtable::ckClear(hashTab)
CkHashtable::ckAddQueryParams(hashTab,CkHttpResponse::ckBodyStr(resp))
accessToken.s = CkHashtable::ckLookupStr(hashTab,"oauth_token")
accessTokenSecret.s = CkHashtable::ckLookupStr(hashTab,"oauth_token_secret")
; The access token + secret is what should be saved and used for
; subsequent REST API calls.
Debug "Access Token = " + accessToken
Debug "Access Token Secret = " + accessTokenSecret
; Save this access token for future calls.
; Just in case we need user_id and screen_name, save those also..
json.i = CkJsonObject::ckCreate()
If json.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkJsonObject::ckAppendString(json,"oauth_token",accessToken)
CkJsonObject::ckAppendString(json,"oauth_token_secret",accessTokenSecret)
fac.i = CkFileAccess::ckCreate()
If fac.i = 0
Debug "Failed to create object."
ProcedureReturn
EndIf
CkFileAccess::ckWriteEntireTextFile(fac,"qa_data/tokens/etsy.json",CkJsonObject::ckEmit(json),"utf-8",0)
Debug "Success."
CkHttp::ckDispose(http)
CkHttpRequest::ckDispose(req)
CkHttpResponse::ckDispose(resp)
CkHashtable::ckDispose(hashTab)
CkStringBuilder::ckDispose(sbUrlForBrowser)
CkSocket::ckDispose(listenSock)
CkSocket::ckDispose(sock)
CkOAuth2::ckDispose(oauth2)
CkStringBuilder::ckDispose(sbResponseHtml)
CkStringBuilder::ckDispose(sbResponse)
CkStringBuilder::ckDispose(sbStartLine)
CkJsonObject::ckDispose(json)
CkFileAccess::ckDispose(fac)
ProcedureReturn
EndProcedure