Facebook OAuth2 for Classic ASP – Step 2

This is the second of a series of two posts to demonstrate implementing OAuth2 Authorization for Facebook in Classic ASP.
(Also see: Facebook OAuth2 for Classic ASP – Step 1)
This ASP is called when Facebook returns a redirect after the FB account owner either grants or denies access.

Here is the ASP source for Step 2:

' Replace with actual values.
' I'm using ngrok to callback to my web server running on localhost..
'RedirectUri = Server.URLEncode("https://www.your-website.com/fb_finishOAuth2.asp")
RedirectUri = Server.URLEncode("https://abca3bde.ngrok.io/fb_finishOAuth2.asp")

' Get the incoming query parameters.
' If access was denied/canceled, we'll get the following params:
'     error_reason=user_denied 
'     error=access_denied 
'     error_description=The+user+denied+your+request.
' If access is granted, we'll get the "state" echoed back to us,
' and we'll also get a "code".

' We'll assume it succeeded..
code = request.querystring("code")
state = request.querystring("state")

' If we wanted, we could verify that the "state" received here is equal to 
' Session("oauth2_state")   

' ------------------------------------
'  Exchanging Code for an Access Token
' ------------------------------------
'  To get an access token, make an HTTP GET request to the following OAuth endpoint:

'  GET https://graph.facebook.com/v2.8/oauth/access_token?
'     client_id={app-id}
'     &redirect_uri={redirect-uri}
'     &client_secret={app-secret}
'     &code={code-parameter}

set http = Server.CreateObject("Chilkat_9_5_0.Http")

success = http.UnlockComponent("Anything for 30-day trial")
If (success <> 1) Then
    Response.Write "<pre>" & Server.HTMLEncode( http.LastErrorText) & "</pre>"
End If

set sbUrl = Server.CreateObject("Chilkat_9_5_0.StringBuilder")
success = sbUrl.Append("https://graph.facebook.com/v2.8/oauth/access_token?client_id={app-id}&redirect_uri={redirect-uri}&client_secret={app-secret}&code={code-parameter}")
replaceCount = sbUrl.Replace("{app-id}",AppId)
replaceCount = sbUrl.Replace("{redirect-uri}",RedirectUri)
replaceCount = sbUrl.Replace("{app-secret}",AppSecret)
replaceCount = sbUrl.Replace("{code-parameter}",code)

respStr = http.QuickGetStr(sbUrl.GetAsString())
If (http.LastMethodSuccess <> 1) Then
    Response.Write "<pre>" & Server.HTMLEncode( http.LastErrorText) & "</pre>"
End If

'  The response string will contain JSON like this:
'  	{
' 	  "access_token": {access-token},
'  	  "token_type": {type},
'  	  "expires_in":	{seconds-til-expiration}
'  	}

set json = Server.CreateObject("Chilkat_9_5_0.JsonObject")
success = json.Load(respStr)

Response.Write "<p>access_token: " & json.StringOf("access_token") & "</p>"
Response.Write "<p>token_type: " & json.StringOf("token_type") & "</p>"
Response.Write "<p>expires_in: " & json.StringOf("expires_in") & "</p>"

' A sample result:
' access_token: EAAFaEtu5GRIBABb...wUXg05RFeaAZDZD
' token_type: bearer
' expires_in: 5180528

Facebook OAuth2 for Classic ASP – Step 1

This is a series of two posts to demonstrate implementing OAuth2 Authorization for Facebook in Classic ASP.
(Also see: Facebook OAuth2 for Classic ASP – Step 2)

The 1st step is to redirect to the Facebook Login Dialog where the Facebook account owner can grant access to the application.  Facebook will then return a response that redirects to your ASP page that implements Step 2.

Here is the ASP for Step 1:

AuthorizationEndpoint = "https://www.facebook.com/dialog/oauth"
TokenEndpoint = "https://graph.facebook.com/oauth/access_token"

' Replace these with actual values.

' Set the Scope to a comma-separated list of permissions the app wishes to request.
' See https://developers.facebook.com/docs/facebook-login/permissions/ for a full list of permissions.
Scope = "public_profile,user_friends,email,user_posts,user_likes,user_photos,publish_actions"

' (State) Chilkat typically uses a 32 random bytes in base64url form.
' However, it can be anything (and any length), and doesn't need to be base64url encoded.
' For this example, I typed some random chars here:
State = "dkrh345y3895hyrtyowiurh3948rhteuirth"

' I'm using ngrok to callback to my web server running on localhost..
'RedirectUri = Server.URLEncode("https://www.your-website.com/fb_finishOAuth2.asp")
RedirectUri = Server.URLEncode("https://abca3bde.ngrok.io/fb_finishOAuth2.asp")

FbAuthUrl = AuthorizationEndpoint & "?response_type=code&scope=" & Scope & "&redirect_uri=" & RedirectUri & "&client_id=" & AppId & "&state=" & State

' Let's save our random state in a session variable.
Session("oauth2_state") = State

Response.Redirect FbAuthUrl

' Note: When I first used ngrok.io, I got the following error from Facebook:
'     Can't Load URL: The domain of this URL isn't included in the app's domains. 
'     To be able to load this URL, add all domains and subdomains of your app to the App Domains field in your app settings.
' To fix, I temporarily changed my Facebook App's Site Url to https://abca3bde.ngrok.io/,
' and then added "abca3bde.ngrok.io" to the list of App Domains.

Upload from ASP code to FTP Server

I get the following support problem about once per week from ASP developers:

The ASP developer wants to upload a file from the client computer where the browser is running, to an FTP server.  He writes ASP code to do the FTP upload, but cannot understand why the file is not found.  The thing the ASP developer doesn’t realize is that his ASP code runs on the web server, not on the client computer where the browser is running.

When I detect this misunderstanding, I’ll be pointing you to this blog post.  Here’s a summary of what you should know:

  1. Your browser, such as Internet Explorer, is running on computer “A”.
  2. The browser communicates with a web server running on computer “B”.
  3. Your ASP code runs within the web server’s worker process on computer “B”.
  4. Your FTP server runs on computer “C”.
  5. Your ASP code (running on computer “B”) is trying to upload a file to the FTP server on computer “C”.  Therefore, it is trying to transfer a file from “B” to “C”.
  6. If you want to upload a file from computer “A” to the FTP server (computer “C”), you need to first do an HTTP upload via HTML using a Form with “<input type=”file” …”.  This uploads from the computer where the browser runs to the web server. Your ASP code then uploads the code from the web server to the FTP server.

Server object error ‘ASP 0177 : 800401f3’

This error is occasionally reported by customers using classic ASP:

Server object error 'ASP 0177 : 800401f3'
Server.CreateObject Failed

The call to CreateObject in ASP looks like this:

set cryptObj = Server.CreateObject("Chilkat.Crypt2")

The problem is that IIS was unable to lookup “Chilkat.Crypt2” in the registry in order to get the location of the DLL file. There are many possible reasons that could cause this failure, and these are listed at the page linked below.

However, it is important to realize that this not a problem with the DLL. If you Google “ASP 0177 : 800401f3” you’ll find that this potential problem is common to all ActiveX components, even ActiveX’s produced by Microsoft. The Chilkat ActiveX DLL’s are standard self-registering ActiveX components. There is nothing special about any Chilkat ActiveX with respect to regsvr32 registration.

A list of possible system problems that might cause this error can be found here:

ASP Upload Script – Receiving Form Text Fields with File Uploads

The Chilkat Upload ActiveX component (which is entirely freeware) provides the capability to receive both form text items as well as file uploads in a single HTTP POST. This post provides an example. First we show the HTML FORM with both text and file input fields. Then we show the ASP script to receive the POST.

More information about the Chilkat ASP Upload component.

The HTML form to send the upload w/ additional form parameters:

<title>ASP Upload with Additional Form Parameters</title>
<h1>ASP Upload with Additional Form Parameters</h1>

<p>Demonstrates how to pass form parameters in addition to uploading a file.  The ASP code that receives this POST will use the form parameters for the Subject, From, To, and Body of an email.  The uploaded file will be attached to the email.</p>

<form method="POST" enctype="multipart/form-data" action = "http://localhost/aspEmailUpload.asp" >

Subject: <input name="subject" type="text" size=40><br><br>
From Email: <input name="fromAddr" type="text" size=40><br><br>
To Email: <input name="toAddr" type="text" size=40><br><br>

<textarea name="emailBody" rows="6" cols="40">
Type the plain-text body of your email here...

Add a file attachment to the email:<br>
<input name=attach1 type=file size=20><br><br>

Attachment Filename in Email: <input name="attachFname" type="text" size=40><br><br>

<input type=submit value="Upload and Email">


ASP Script to Receive the Upload:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

'  The Chilkat ASP Upload component is freeware.
'  The UploadRcv object receives uploads in ASP.
'  It can be used to save uploaded files to the web server,
'  or to save directly to memory for immediate access.
'  This example streams an uploaded file into memory.
set receiver = Server.CreateObject("Chilkat.UploadRcv")

'  Set a timeout just in case something hangs
'  This is a 20-second timeout:
receiver.IdleTimeoutMs = 20000

'  Consume the upload.  Files are streamed to the UploadDir
success = receiver.Consume()

If (success = 1) And (receiver.NumFilesReceived > 0) Then

    ' The Chilkat Upload component is freeware.  
    ' For demonstration purposes, this example receives the upload 
    ' form params and file and uses the data to send an email.
    ' The Chilkat Email component is not freeware.
	'  The mailman object is used for sending and receiving email.
	set mailman = Server.CreateObject("Chilkat.MailMan2")

	'  Any string argument automatically begins the 30-day trial.
	success = mailman.UnlockComponent("30-day trial")
	If (success = 1) Then

		'  Set the SMTP server.
		mailman.SmtpHost = "smtp.chilkatsoft.com"

		'  Set the SMTP login/password (if required)
		mailman.SmtpUsername = "myLogin"
		mailman.SmtpPassword = "myPassword"

		'  Create a new email object
		set email = Server.CreateObject("Chilkat.Email2")

		' We don't use Request.Form.  Instead use receiver.GetParam
		' because the receiver object received the HTTP request.
		email.Subject = receiver.GetParam("subject")
		Response.Write "Subject: " & email.Subject & "<p>"
		email.Body = receiver.GetParam("emailBody")
		email.From = receiver.GetParam("fromAddr")
		email.AddMultipleTo receiver.GetParam("toAddr")
		' Add the uploaded file as an attachment:
		email.AddDataAttachment receiver.GetFileData(0), receiver.GetParam("attachFname")

		'  Call SendEmail to connect to the SMTP server and send.
		'  The connection (i.e. session) to the SMTP server remains
		'  open so that subsequent SendEmail calls may use the
		'  same connection.
		success = mailman.SendEmail(email)
		If (success = 1) Then
			'  Some SMTP servers do not actually send the email until
			'  the connection is closed.  In these cases, it is necessary to
			'  call CloseSmtpConnection for the mail to be  sent.
			'  Most SMTP servers send the email immediately, and it is
			'  not required to close the connection.  We'll close it here
			'  for the example:
			success = mailman.CloseSmtpConnection()
			If (success <> 1) Then
			    Response.Write "Connection to SMTP server not closed cleanly." & "<br>"
			End If

			Response.Write "Mail Sent!" & "<br>"
		    Response.Write mailman.LastErrorText & "<br>"
		End If
	    Response.Write "Component unlock failed"
	end if
	Response.Write "Failed to receive upload."
end if