Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The following describes, as a set of examples, the full process of requesting access from the user, acquiring an authentication code and access token, and downloading and opening the user’s Scholar Snapp (SDS) file. The code is presented as standard Microsoft .NET 4.5 C# code, but any code that performs the required requests may be used.

“Client Application” Settings

To understand the example, it is important to define some basic variables. The scholarship provider application (or “client application”) is located at http://www.example.org/. The application has a page at http://www.example.org/scholarsnappconnect which is used as the redirect URL. The application’s client ID is “example.org” and its client secret is “b4703df5f66f46ad9c9ff130a46c5f00”.

First HTTP Request: Requesting Access from the Applicant

The scholarship provider application presents a button to the user, with the following HTML markup. Note that any method that directs the user’s browser to the required URL will work.

Code Block
<a ref="https://auth.scholarsnapp.org/oauth/Authorize?response_type=code&client_id=example.org&redirect_uri=http%3A%2F%2Fwww.example.org%2Fscholarsnappconnect&state=5CD4CED7-965C-452B-A5F5-AC957DAD7542">Connect to Scholar Snapp</a>

The link could be on an image (e.g., the Scholar Snapp logo) or could be styled in order to fit the client application’s style guide.

Second HTTP Request: Processing Scholar Snapp Import

If the user authorizes the client application to access their profile, the user’s browser will be redirected to:

http://www.example.org/scholarsnappconnect?code=SplxlOBeZQQYbYS6WxSbIA&state=5CD4CED7-965C-452B-A5F5-AC957DAD7542.

Note that the “state” parameter is returned exactly as passed to the Scholar Snapp websiteCentral API.

That page should make a POST request to the Scholar Snapp /OAuthoauth/Tokentoken endpoint and parse the JSON response, for example using code similar to the following:

Code Block
languagec#
string client_id = "snapp_auth_test";
string client_secret = "09c74ca87c55468daefe68be15d2ba69"; 
string scholarSnappBaseUri = "https://auth.scholarsnapp.org";
string clientAppRedirectUri = "http://www.example.org/scholarsnappconnect";

var grantType = "authorization_code";
var url = scholarSnappBaseUri + "/oauth/Token";

// Create the form body for the POST 
var data = new StringBuilder();
data.Append("grant_type=" + Uri.EscapeDataString(grantType)); 
data.Append("&code=" + Uri.EscapeDataString(code)); 
data.Append("&redirect_uri=" + Uri.EscapeDataString(clientAppRedirectUri));

using (var webClient = new WebClient())
{
	webClient.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes(client_id + ":" + client_secret)));
	webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");

	var json = webClient.UploadString(url, data.ToString());

	JavaScriptSerializer ser = new JavaScriptSerializer(); 
	Dictionary<string, object> x = (Dictionary<string, object>)ser.DeserializeObject(json);
	var accessToken = x["access_token"].ToString();
	var expiresIn = int.Parse(x["expires_in"].ToString()); // In seconds
}

A real application will need to handle potential errors or exceptions that occur during any of the requests presented in this example.

Once the application has the access token, a simple GET request to the API endpoint will retrieve the Scholar Snapp Profile for the user:

Code Block
languagec#
string scholarSnappBaseUri = "https://auth.scholarsnapp.org"; 
var url = scholarSnappBaseUri + "/api/profile/snappfile?version=4"; 
byte[] scholarSnappFile;

using (var webClient = new WebClient())
{
	webClient.Headers.Add("Authorization", "Bearer " + authToken);

	scholarSnappFile = webClient.DownloadData(url);
	// The scholarSnappFile variable contains the raw byte stream of the compressed SDS file.
}

And the application can read it using any XML library:

Code Block
languagec#
XNamespace sds = XNamespace.Get("http://scholarshipproviders.org/scholarshipapplication");

var profile = new ScholarSnappProfile();

using (var memStream = new MemoryStream(scholarSnappFile))
{
	if (memStream.CanSeek)
	memStream.Seek(0, SeekOrigin.Begin);
	using (var reader = new XmlTextReader(memStream))
	{
		reader.MoveToContent();
		var xml = XDocument.Load(reader);
		var documentElement = xml.Element(sds + "ScholarshipApplication"); 
		var contextElement = documentElement.Element(sds + "Context");
		var studentElement = documentElement.Element(sds + "Student"); 
		var element = studentElement.Element(sds + "Name");
		if (element != null)
		{
			profile.FirstName = GetValueIfPresent<string>(element.Element(sds + "FirstName"));
			profile.MiddleName = GetValueIfPresent<string>(element.Element(sds + "MiddleName"));
			profile.LastName = GetValueIfPresent<string>(element.Element(sds + "LastName"));
			profile.Suffix = GetValueIfPresent<string>(element.Element(sds + "Suffix"));
			profile.PreferredName = GetValueIfPresent<string>(element.Element(sds + "PreferredName"));
		}

		element = studentElement.Element(sds + "ContactInfo"); 
		if (element != null)
		{
			profile.Email = GetValueIfPresent<string>(element.Element(sds + "Email"));
		}
		profile.DateOfBirth = GetValueIfPresent<DateTime?>(studentElement.Element(sds + "BirthDate"));
		profile.Sex = GetValueIfPresent<string>(studentElement.Element(sds + "Gender"));
		reader.Close(); 
		memStream.Close();
	}
}

GetValueIfPresent is a convenience method for reading an XML element that may or may not exist. It is reproduced below.

Code Block
languagec#
private T GetValueIfPresent<T>(XElement element)
{
	if (element == null || element.IsEmpty || string.IsNullOrWhiteSpace(element.Value)) return default(T);

	var underlyingType = Nullable.GetUnderlyingType(typeof(T)); 
	if (underlyingType != null)
	{
		return (T)Convert.ChangeType(element.Value, underlyingType);
	}
	return (T)Convert.ChangeType(element.Value, typeof(T));
}

In this case, only a small subset of properties are read from the file. In real code, many more properties would be read and additional error handling code would be present.

To send any updated information back to Scholar Snapp to allow the applicant to update their profile, the same API endpoint can be used with a POST request:

Code Block
languagec#
var authToken = (string)Session["authToken"];
var url = scholarSnappBaseUri + "/api/profile/snappfile?version=4";

string message = null;
byte[] profileData = GenerateSnappFile(); 
using (var webClient = new WebClient())
{
	webClient.Headers.Add("Authorization", "Bearer " + authToken);
	var json = Encoding.UTF8.GetString(webClient.UploadData(url, profileData));
	JavaScriptSerializer ser = new JavaScriptSerializer(); 
	Dictionary<string, object> x = (Dictionary<string, object>)ser.DeserializeObject(json); 
	message = x["message"].ToString();
}

The GenerateSnappFile method would need to take your internal data representation and produce a byte array representing the Scholar Snapp XML. An implementation covering a small portion of the Scholar Snapp Profile is shown below.

Code Block
public byte[] GenerateSnappFile(ApplicantProfile profile)
{
	var xmlns = XNamespace.Get("http://scholarshipproviders.org/scholarshipapplication1.0");
	XDocument xmlDocument = new XDocument(); xmlDocument.Declaration = new XDeclaration("1.0", "UTF-8", "");

	var documentElement = new XElement(sds + "ScholarshipApplication", new XAttribute(XNamespace.Xmlns + "sds", sds));
	xmlDocument.Add(documentElement);

	var contextElement = new XElement(sds + "Context");
	contextElement.Add(new XElement(sds + "Created", DateTime.Now.ToString("O")));
	contextElement.Add(new XElement(sds + "CreatedBy", "Scholar Snapp API Sample"));
	contextElement.Add(new XElement(sds + "LastUpdated", DateTime.Now.ToString("O")));
	contextElement.Add(new XElement(sds + "LastUpdatedBy", "Scholar Snapp API Sample"));
	contextElement.Add(new XElement(sds + "ApplicationLanguage", "en-US"));
	documentElement.Add(contextElement);

	var studentElement = new XElement(sds + "Student"); 
	var nameElement = new XElement(sds + "Name");
	nameElement.Add(new XElement(sds + "FirstName", profile.FirstName)); 
	if (!string.IsNullOrEmpty(profile.MiddleName)) nameElement.Add(new XElement(sds + "MiddleName", profile.MiddleName)); 
	nameElement.Add(new XElement(sds + "LastName", profile.LastName));
	if (!string.IsNullOrEmpty(profile.PreferredName)) nameElement.Add(new XElement(sds + "PreferredName", profile.PreferredName)); 
	studentElement.Add(nameElement);

	var contactInfoElement = new XElement(sds + "ContactInfo"); 
	contactInfoElement.Add(new XElement(sds + "Email", profile.Email));
	studentElement.Add(contactInfoElement);

	if (profile.DateOfBirth.HasValue) studentElement.Add(new XElement(sds + "BirthDate", profile.DateOfBirth.Value.ToString("yyyy'-'MM'-'ddKK"))); 
	
	documentElement.Add(studentElement);
	
	using (var stream = new MemoryStream())
	using (var writer = XmlWriter.Create(stream))
	{
		xmlDocument.WriteTo(writer); 
		writer.Close(); 
		stream.Close();
		return stream.ToArray();
	}
}


Include Page
_API Section Sidebar
_API Section Sidebar