OAuth is an option to work alongside Forms Authentication to enable users to log in with credentials from another site such as Facebook or Twitter.
Developers can extend OAuth support to other OAuth services or even add further functionality to the existing OAuth clients. The providers that come with Telligent Community collect only the information needed from a user to log them into the site. They do not support posting content back to Facebook or Twitter. However, a custom provider could be created to extend the default functionality.
[toc]
Overview of Existing OAuth Clients
Telligent Community has a number of OAuth clients that can be managed simply by enabling and configuring the corresponding plugin. These are all implementations of the IOAuthClient interface.
- Facebook authentication
- Google authentication
- LinkedIn authentication
- Live Connect authentication
- Salesforce authentication
- Twitter authentication
Creating an OAuth Client
Implementing IOAuthClient provides the capability to use a third party service to provide user authentication. Our sample implementation will be based on Pinterest Oauth process. Usage of this example requires you to create an app in Pinterest's Dev Center so you can obtain a client key and secret. You can find more details and set up a client app here: https://developers.pinterest.com/docs/api/overview.
Required DLLs:
- Telligent.Evolution.Components
- Telligent.Evolution.Controls
Defining Client Properties
We define several properties statically from the interface for later use.
Client Name is the name used by Telligent Community when referring to your client - for instance, as mouseover text on your icon.
public string ClientName { get { return "Pinterest"; } }
Client Type must be a unique value among all OAuth Clients, identifying your client.
public string ClientType { get { return "pinterest"; } }
The AuthorizeBaseUrl and AccessTokenUrl should be specified by the third party service you are authenticating against.
public virtual string AuthorizeBaseUrl { get { return "https://api.pinterest.com/oauth"; } } public virtual string AccessTokenUrl { get { return "https://api.pinterest.com/v1/oauth/token"; } }
Theme Color is a hex-based color value that provides additional identity to your client for display purposes.
public string ThemeColor { get { return "BD081C"; } }
ConsumerKey and ConsumerSecret should be generated for you when you create an app with the external service. Use of IConfigurablePlugin is recommended here, so that the values can be edited via Administration in your Telligent Community site.
public virtual string ConsumerKey { get { return Configuration.GetString("ConsumerKey"); } } public virtual string ConsumerSecret { get { return Configuration.GetString("ConsumerSecret"); } }
Privacy Statement is simply a text field to allow you to provide legal information to your users regarding use of this login method.
// Your privacy statement should include what privacy information // is being collected about the user using this OAuth client. public string Privacy { get { return "Privacy statement"; } }
Enabled and CallbackUrl are both managed by Telligent Community and may be deprecated and removed in future releases.
// Does nothing, potentially will be removed in a future release public bool Enabled { get { return true; } } // Implemented simply as an auto-property, this may also be removed in the future private string _callbackUrl; public virtual string CallbackUrl { get { return _callbackUrl; } set { if (!string.IsNullOrEmpty(value) && value.StartsWith("http:")) _callbackUrl = "https" + value.Substring(4); else _callbackUrl = value; } }
ClientLogoutScript allows some additional actions to be taken when logout occurs in Telligent Community. This is html that will be executed when a client logs out after being logged in through this OAuth client. Use this to perform further actions if needed.
public string ClientLogoutScript { get { return ""; } }
IconUrl is used to display an image when presenting your OAuth client as a login option. It returns the url to an image representing your OAuth provider. You can link to an external url or add an image to Centralized File Storage and link to that.
public string IconUrl { get { return "https://developers.pinterest.com/static/img/badge.svg"; } }
The GetAuthorizationLink() method simply provides the URL to be used to initially request an access token to the external site. Again, the query string properties and other formatting would be specified by the third party service. Here, we will not be interacting with any Pinterest data other than the user's identity, so we only need the "read_public" permission.
public string GetAuthorizationLink() { return string.Format("{0}/?client_id={1}&redirect_uri={2}&response_type=code&scope=read_public", AuthorizeBaseUrl, ConsumerKey, Globals.UrlEncode(CallbackUrl)); }
Processing Login Context
The main job of the client is to process a user's login based on information in the response from the external service - in this case, Pinterest. First we perform some sanity checks. The existence of errors in the Query string may be dependent on your third party service's implementation.
public OAuthData ProcessLogin(HttpContextBase context) { if (!Enabled || context.Request.QueryString["error"] != null) FailedLogin();
Next we need to check for the data we need for verification of the user login - in this case, an authentication code to be used to gain an access token.
if (context.Request.QueryString["code"] == null) FailedLogin(); string authorizationCode = context.Request.QueryString["code"];
With the authentication code in hand, we then request the access token and make a sanity check.
string token = GetAccessToken(authorizationCode); if (string.IsNullOrEmpty(token)) FailedLogin();
Finally, we query the Pinterest API for user data and return an OAuthData object populated with that data:
return GetUserData(token); }
Getting the Access Token
Getting the access token requires another HTTP request to Pinterest. First we set up the request using the required parameters, data, and request properties. (Other services may vary on the data required here).
private string GetAccessToken(string authorizationCode) { // Use the authorization code to request an access token. string postData = string.Format("grant_type=authorization_code&client_id={0}&client_secret={1}&code={2}", ConsumerKey, ConsumerSecret, authorizationCode); var webClient = new WebClient(); string responseData; try { webClient.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
We then make the request and read the data received.
responseData = webClient.UploadString(AccessTokenUrl, postData); } catch (Exception ex) { throw new ApplicationException("OAuth Web Request Failed", ex); }
Finally, we can extract the access token from the data and return it.
if (responseData.Length > 0) { //Store the returned access_token dynamic accessResponse = new JavaScriptSerializer().DeserializeObject(responseData); if (accessResponse["access_token"] != null) return accessResponse["access_token"]; } return null; }
Getting Your User's Data
Now we at last have full authorizations to make calls to the Pinterest API on our user's behalf. Our main concern for pure authentication purposes is identifying the user uniquely so Telligent Community can track them internally. First we've created a few private classes to help with parsing from json. You can also use the c# dynamic, which we've done above.
private class PinterestUserDetails { public PinterestData data; } private class PinterestData { public string url { get; set; } public string first_name { get; set; } public string last_name { get; set; } public string id { get; set; } }
To get user data, we make a call to the /me endpoint with our access token. Similar to above, we generate the web request, and receive and read the response.
// Use Pinterest API to get details about the user string pinterestUrl = string.Format("https://api.pinterest.com/v1/me?access_token={0}", token); var webClient = new WebClient(); string responseData; try { responseData = webClient.DownloadString(pinterestUrl); } catch (Exception ex) { throw new ApplicationException("OAuth Web Request Failed", ex); }
Parsing the response into our private classes allows easy access to populate the OAuthData return class:
if (responseData.Length > 0) { //Store the returned access_token PinterestUserDetails userDetails = new JavaScriptSerializer().Deserialize<PinterestUserDetails>(responseData); var data = new OAuthData { ClientId = userDetails.data.id, ClientType = ClientType, UserName = string.Format("{0}{1}", userDetails.data.first_name, userDetails.data.last_name), CommonName = string.Format("{0} {1}", userDetails.data.first_name, userDetails.data.last_name) }; return data; } return null;
Seeing the Results
Once you've compiled and added the plugin to your Telligent Community instance, you can enable it, input the key and secret, and your users will be able to sign in with Pinterest. There will be an icon on the sign in page, and clicking will begin the login process.
Full Sample
View the full sample project on github: https://github.com/Telligent/Pinterest-OAuth-Client