Hello once again...
I got a new task in which i had to get the contact list from the yahoo, gmail and hotmail. Here is way in which i have completed this task:
1. Create an empty website in Visual Studio.
2. Add reference to "DotNetOpenAuth.dll", "Google.GData.Client.dll", "Google.GData.Contacts.dll", Google.GData.Extensions.dll
3. add a class named as "GetYahooContacts.cs"
Here is code of it:
4. Create another class named as "OAuthBase.cs"
its code is:
its code is :
HTML code is :
and code behind code is:
And there will be some settings in web.config also :
I got a new task in which i had to get the contact list from the yahoo, gmail and hotmail. Here is way in which i have completed this task:
1. Create an empty website in Visual Studio.
2. Add reference to "DotNetOpenAuth.dll", "Google.GData.Client.dll", "Google.GData.Contacts.dll", Google.GData.Extensions.dll
3. add a class named as "GetYahooContacts.cs"
Here is code of it:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using DotNetOpenAuth.OAuth; using OAuth; using System.Xml; using System.Collections; using DotNetOpenAuth.ComponentModel; using System.Web.UI; using System.Web.UI.WebControls; using System.Net; using System.Web; namespace Yahoo { public class GetYahooContacts { public string ConsumerKey { get { return "***********"; } } public string ConsumerSecret { get { return "***********"; } } private string _ouathVerifier; public string OauthVerifier { get { try { if (!string.IsNullOrEmpty(_ouathVerifier)) return _ouathVerifier; else return string.Empty; } catch { return string.Empty; } } set { _ouathVerifier = value; } } private string _oauthToken; public string OauthToken { get { if (!string.IsNullOrEmpty(_oauthToken)) return _oauthToken; else return string.Empty; } set { _oauthToken = value; } } private string _oauthTokenSecret; public string OauthTokenSecret { get { if (!string.IsNullOrEmpty(_oauthTokenSecret)) return _oauthTokenSecret; else return string.Empty; } set { _oauthTokenSecret = value; } } private string _oauthSessionHandle; public string OauthSessionHandle { get { if (!string.IsNullOrEmpty(_oauthSessionHandle)) return _oauthSessionHandle; else return string.Empty; } set { _oauthSessionHandle = value; } } private string _oauthYahooGuid; public string OauthYahooGuid { get { try { if (!string.IsNullOrEmpty(_oauthYahooGuid)) return _oauthYahooGuid; else return string.Empty; } catch { return string.Empty; } } set { _oauthYahooGuid = value; } } public string authorization(string returnUrl) { string authorizationUrl = string.Empty; authorizationUrl = GetRequestToken(returnUrl); return authorizationUrl; } public List<string> GetContacts() { GetAccessToken(OauthToken, OauthVerifier); return RetriveContacts(); } private string GetRequestToken(string returnUrl) { string authorizationUrl = string.Empty; OAuthBase oauth = new OAuthBase(); Uri uri = new Uri("https://api.login.yahoo.com/oauth/v2/get_request_token"); string nonce = oauth.GenerateNonce(); string timeStamp = oauth.GenerateTimeStamp(); string normalizedUrl; string normalizedRequestParameters; string sig = oauth.GenerateSignature(uri, ConsumerKey, ConsumerSecret, string.Empty, string.Empty, "GET", timeStamp, nonce, OAuthBase.SignatureTypes.PLAINTEXT, out normalizedUrl, out normalizedRequestParameters); //OAuthBase.SignatureTypes.HMACSHA1 StringBuilder sbRequestToken = new StringBuilder(uri.ToString()); sbRequestToken.AppendFormat("?oauth_nonce={0}&", nonce); sbRequestToken.AppendFormat("oauth_timestamp={0}&", timeStamp); sbRequestToken.AppendFormat("oauth_consumer_key={0}&", ConsumerKey); sbRequestToken.AppendFormat("oauth_signature_method={0}&", "PLAINTEXT"); //HMAC-SHA1 sbRequestToken.AppendFormat("oauth_signature={0}&", sig); sbRequestToken.AppendFormat("oauth_version={0}&", "1.0"); sbRequestToken.AppendFormat("oauth_callback={0}", HttpUtility.UrlEncode(returnUrl)); //Response.Write(sbRequestToken.ToString()); //Response.End(); try { string returnStr = string.Empty; string[] returnData; HttpWebRequest req = (HttpWebRequest)WebRequest.Create(sbRequestToken.ToString()); HttpWebResponse res = (HttpWebResponse)req.GetResponse(); StreamReader streamReader = new StreamReader(res.GetResponseStream()); returnStr = streamReader.ReadToEnd(); returnData = returnStr.Split(new Char[] { '&' }); //Response.Write(returnStr); int index; if (returnData.Length > 0) { //index = returnData[0].IndexOf("="); //string oauth_token = returnData[0].Substring(index + 1); //Session["Oauth_Token"] = oauth_token; index = returnData[1].IndexOf("="); string oauth_token_secret = returnData[1].Substring(index + 1); OauthTokenSecret = oauth_token_secret; //index = returnData[2].IndexOf("="); //int oauth_expires_in; //Int32.TryParse(returnData[2].Substring(index + 1), out oauth_expires_in); //Session["Oauth_Expires_In"] = oauth_expires_in; index = returnData[3].IndexOf("="); string oauth_request_auth_url = returnData[3].Substring(index + 1); authorizationUrl = HttpUtility.UrlDecode(oauth_request_auth_url); } } catch (WebException ex) { //Response.Write(ex.Message); } return authorizationUrl; } private void GetAccessToken(string oauth_token, string oauth_verifier) { OAuthBase oauth = new OAuthBase(); Uri uri = new Uri("https://api.login.yahoo.com/oauth/v2/get_token"); string nonce = oauth.GenerateNonce(); string timeStamp = oauth.GenerateTimeStamp(); string sig = ConsumerSecret + "%26" + OauthTokenSecret; StringBuilder sbAccessToken = new StringBuilder(uri.ToString()); sbAccessToken.AppendFormat("?oauth_consumer_key={0}&", ConsumerKey); sbAccessToken.AppendFormat("oauth_signature_method={0}&", "PLAINTEXT"); //HMAC-SHA1 sbAccessToken.AppendFormat("oauth_signature={0}&", sig); sbAccessToken.AppendFormat("oauth_timestamp={0}&", timeStamp); sbAccessToken.AppendFormat("oauth_version={0}&", "1.0"); sbAccessToken.AppendFormat("oauth_token={0}&", oauth_token); sbAccessToken.AppendFormat("oauth_nonce={0}&", nonce); sbAccessToken.AppendFormat("oauth_verifier={0}", oauth_verifier); //Response.Write(sbAccessToken.ToString()); //Response.End(); try { string returnStr = string.Empty; string[] returnData; HttpWebRequest req = (HttpWebRequest)WebRequest.Create(sbAccessToken.ToString()); HttpWebResponse res = (HttpWebResponse)req.GetResponse(); StreamReader streamReader = new StreamReader(res.GetResponseStream()); returnStr = streamReader.ReadToEnd(); returnData = returnStr.Split(new Char[] { '&' }); //Response.Write(returnStr); //Response.End(); int index; if (returnData.Length > 0) { index = returnData[0].IndexOf("="); OauthToken = returnData[0].Substring(index + 1); index = returnData[1].IndexOf("="); string oauth_token_secret = returnData[1].Substring(index + 1); OauthTokenSecret = oauth_token_secret; //index = returnData[2].IndexOf("="); //int oauth_expires_in; //Int32.TryParse(returnData[2].Substring(index + 1), out oauth_expires_in); index = returnData[3].IndexOf("="); string oauth_session_handle = returnData[3].Substring(index + 1); OauthSessionHandle = oauth_session_handle; //index = returnData[4].IndexOf("="); //int oauth_authorization_expires_in; //Int32.TryParse(returnData[4].Substring(index + 1), out oauth_authorization_expires_in); index = returnData[5].IndexOf("="); string xoauth_yahoo_guid = returnData[5].Substring(index + 1); OauthYahooGuid = xoauth_yahoo_guid; } } catch (WebException ex) { //Response.Write(ex.Message); } } private List<string> RetriveContacts() { OAuthBase oauth = new OAuthBase(); Uri uri = new Uri("http://social.yahooapis.com/v1/user/" + OauthYahooGuid + "/contacts?format=XML"); string nonce = oauth.GenerateNonce(); string timeStamp = oauth.GenerateTimeStamp(); string normalizedUrl; string normalizedRequestParameters; string sig = oauth.GenerateSignature(uri, ConsumerKey, ConsumerSecret, OauthToken, OauthTokenSecret, "GET", timeStamp, nonce, OAuthBase.SignatureTypes.HMACSHA1, out normalizedUrl, out normalizedRequestParameters); StringBuilder sbGetContacts = new StringBuilder(uri.ToString()); //Response.Write("URL: " + sbGetContacts.ToString()); //Response.End(); try { string returnStr = string.Empty; HttpWebRequest req = (HttpWebRequest)WebRequest.Create(sbGetContacts.ToString()); req.Method = "GET"; string authHeader = "Authorization: OAuth " + "realm=\"yahooapis.com\"" + ",oauth_consumer_key=\"" + ConsumerKey + "\"" + ",oauth_nonce=\"" + nonce + "\"" + ",oauth_signature_method=\"HMAC-SHA1\"" + ",oauth_timestamp=\"" + timeStamp + "\"" + ",oauth_token=\"" + OauthToken + "\"" + ",oauth_version=\"1.0\"" + ",oauth_signature=\"" + HttpUtility.UrlEncode(sig) + "\""; //Response.Write("</br>Headers: " + authHeader); req.Headers.Add(authHeader); HttpWebResponse res = (HttpWebResponse)req.GetResponse(); StreamReader streamReader = new StreamReader(res.GetResponseStream()); returnStr = streamReader.ReadToEnd(); XmlDocument xmldoc = new XmlDocument(); xmldoc.LoadXml(returnStr); XmlNodeList elemList = xmldoc.DocumentElement.GetElementsByTagName("fields"); List<string> emails = new List<string>(); for (int i = 0; i < elemList.Count; i++) { if (elemList[i].ChildNodes[1].InnerText == "email") emails.Add(elemList[i].ChildNodes[2].InnerText); //Response.Write(elemList[i].ChildNodes[2].InnerText + "<br/>"); } return emails; } catch (WebException ex) { return null; } } } }
4. Create another class named as "OAuthBase.cs"
its code is:
5. create another class named "WindowsLiveLogin.cs"using System; using System.Security.Cryptography; using System.Collections.Generic; using System.Text; using System.Web; namespace OAuth { public class OAuthBase { /// <summary> /// Provides a predefined set of algorithms that are supported officially by the protocol /// </summary> public enum SignatureTypes { HMACSHA1, PLAINTEXT, RSASHA1 } /// <summary> /// Provides an internal structure to sort the query parameter /// </summary> protected class QueryParameter { private string name = null; private string value = null; public QueryParameter(string name, string value) { this.name = name; this.value = value; } public string Name { get { return name; } } public string Value { get { return value; } } } /// <summary> /// Comparer class used to perform the sorting of the query parameters /// </summary> protected class QueryParameterComparer : IComparer<QueryParameter> { #region IComparer<QueryParameter> Members public int Compare(QueryParameter x, QueryParameter y) { if (x.Name == y.Name) { return string.Compare(x.Value, y.Value); } else { return string.Compare(x.Name, y.Name); } } #endregion } protected const string OAuthVersion = "1.0"; protected const string OAuthParameterPrefix = "oauth_"; // // List of know and used oauth parameters' names // protected const string OAuthConsumerKeyKey = "oauth_consumer_key"; protected const string OAuthCallbackKey = "oauth_callback"; protected const string OAuthVersionKey = "oauth_version"; protected const string OAuthSignatureMethodKey = "oauth_signature_method"; protected const string OAuthSignatureKey = "oauth_signature"; protected const string OAuthTimestampKey = "oauth_timestamp"; protected const string OAuthNonceKey = "oauth_nonce"; protected const string OAuthTokenKey = "oauth_token"; protected const string OAuthTokenSecretKey = "oauth_token_secret"; protected const string HMACSHA1SignatureType = "HMAC-SHA1"; protected const string PlainTextSignatureType = "PLAINTEXT"; protected const string RSASHA1SignatureType = "RSA-SHA1"; protected Random random = new Random(); protected string unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~"; /// <summary> /// Helper function to compute a hash value /// </summary> /// <param name="hashAlgorithm">The hashing algoirhtm used. If that algorithm needs some initialization, like HMAC and its derivatives, they should be initialized prior to passing it to this function</param> /// <param name="data">The data to hash</param> /// <returns>a Base64 string of the hash value</returns> private string ComputeHash(HashAlgorithm hashAlgorithm, string data) { if (hashAlgorithm == null) { throw new ArgumentNullException("hashAlgorithm"); } if (string.IsNullOrEmpty(data)) { throw new ArgumentNullException("data"); } byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(data); byte[] hashBytes = hashAlgorithm.ComputeHash(dataBuffer); return Convert.ToBase64String(hashBytes); } /// <summary> /// Internal function to cut out all non oauth query string parameters (all parameters not begining with "oauth_") /// </summary> /// <param name="parameters">The query string part of the Url</param> /// <returns>A list of QueryParameter each containing the parameter name and value</returns> private List<QueryParameter> GetQueryParameters(string parameters) { if (parameters.StartsWith("?")) { parameters = parameters.Remove(0, 1); } List<QueryParameter> result = new List<QueryParameter>(); if (!string.IsNullOrEmpty(parameters)) { string[] p = parameters.Split('&'); foreach (string s in p) { if (!string.IsNullOrEmpty(s) && !s.StartsWith(OAuthParameterPrefix)) { if (s.IndexOf('=') > -1) { string[] temp = s.Split('='); result.Add(new QueryParameter(temp[0], temp[1])); } else { result.Add(new QueryParameter(s, string.Empty)); } } } } return result; } /// <summary> /// This is a different Url Encode implementation since the default .NET one outputs the percent encoding in lower case. /// While this is not a problem with the percent encoding spec, it is used in upper case throughout OAuth /// </summary> /// <param name="value">The value to Url encode</param> /// <returns>Returns a Url encoded string</returns> protected string UrlEncode(string value) { StringBuilder result = new StringBuilder(); foreach (char symbol in value) { if (unreservedChars.IndexOf(symbol) != -1) { result.Append(symbol); } else { result.Append('%' + String.Format("{0:X2}", (int)symbol)); } } return result.ToString(); } /// <summary> /// Normalizes the request parameters according to the spec /// </summary> /// <param name="parameters">The list of parameters already sorted</param> /// <returns>a string representing the normalized parameters</returns> protected string NormalizeRequestParameters(IList<QueryParameter> parameters) { StringBuilder sb = new StringBuilder(); QueryParameter p = null; for (int i = 0; i < parameters.Count; i++) { p = parameters[i]; sb.AppendFormat("{0}={1}", p.Name, p.Value); if (i < parameters.Count - 1) { sb.Append("&"); } } return sb.ToString(); } /// <summary> /// Generate the signature base that is used to produce the signature /// </summary> /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> /// <param name="consumerKey">The consumer key</param> /// <param name="token">The token, if available. If not available pass null or an empty string</param> /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> /// <param name="signatureType">The signature type. To use the default values use <see cref="OAuthBase.SignatureTypes">OAuthBase.SignatureTypes</see>.</param> /// <returns>The signature base</returns> public string GenerateSignatureBase(Uri url, string consumerKey, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, string signatureType, out string normalizedUrl, out string normalizedRequestParameters) { if (token == null) { token = string.Empty; } if (tokenSecret == null) { tokenSecret = string.Empty; } if (string.IsNullOrEmpty(consumerKey)) { throw new ArgumentNullException("consumerKey"); } if (string.IsNullOrEmpty(httpMethod)) { throw new ArgumentNullException("httpMethod"); } if (string.IsNullOrEmpty(signatureType)) { throw new ArgumentNullException("signatureType"); } normalizedUrl = null; normalizedRequestParameters = null; List<QueryParameter> parameters = GetQueryParameters(url.Query); parameters.Add(new QueryParameter(OAuthVersionKey, OAuthVersion)); parameters.Add(new QueryParameter(OAuthNonceKey, nonce)); parameters.Add(new QueryParameter(OAuthTimestampKey, timeStamp)); parameters.Add(new QueryParameter(OAuthSignatureMethodKey, signatureType)); parameters.Add(new QueryParameter(OAuthConsumerKeyKey, consumerKey)); if (!string.IsNullOrEmpty(token)) { parameters.Add(new QueryParameter(OAuthTokenKey, token)); } parameters.Sort(new QueryParameterComparer()); normalizedUrl = string.Format("{0}://{1}", url.Scheme, url.Host); if (!((url.Scheme == "http" && url.Port == 80) || (url.Scheme == "https" && url.Port == 443))) { normalizedUrl += ":" + url.Port; } normalizedUrl += url.AbsolutePath; normalizedRequestParameters = NormalizeRequestParameters(parameters); StringBuilder signatureBase = new StringBuilder(); signatureBase.AppendFormat("{0}&", httpMethod.ToUpper()); signatureBase.AppendFormat("{0}&", UrlEncode(normalizedUrl)); signatureBase.AppendFormat("{0}", UrlEncode(normalizedRequestParameters)); return signatureBase.ToString(); } /// <summary> /// Generate the signature value based on the given signature base and hash algorithm /// </summary> /// <param name="signatureBase">The signature based as produced by the GenerateSignatureBase method or by any other means</param> /// <param name="hash">The hash algorithm used to perform the hashing. If the hashing algorithm requires initialization or a key it should be set prior to calling this method</param> /// <returns>A base64 string of the hash value</returns> public string GenerateSignatureUsingHash(string signatureBase, HashAlgorithm hash) { return ComputeHash(hash, signatureBase); } /// <summary> /// Generates a signature using the HMAC-SHA1 algorithm /// </summary> /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> /// <param name="consumerKey">The consumer key</param> /// <param name="consumerSecret">The consumer seceret</param> /// <param name="token">The token, if available. If not available pass null or an empty string</param> /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> /// <returns>A base64 string of the hash value</returns> public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, out string normalizedUrl, out string normalizedRequestParameters) { return GenerateSignature(url, consumerKey, consumerSecret, token, tokenSecret, httpMethod, timeStamp, nonce, SignatureTypes.HMACSHA1, out normalizedUrl, out normalizedRequestParameters); } /// <summary> /// Generates a signature using the specified signatureType /// </summary> /// <param name="url">The full url that needs to be signed including its non OAuth url parameters</param> /// <param name="consumerKey">The consumer key</param> /// <param name="consumerSecret">The consumer seceret</param> /// <param name="token">The token, if available. If not available pass null or an empty string</param> /// <param name="tokenSecret">The token secret, if available. If not available pass null or an empty string</param> /// <param name="httpMethod">The http method used. Must be a valid HTTP method verb (POST,GET,PUT, etc)</param> /// <param name="signatureType">The type of signature to use</param> /// <returns>A base64 string of the hash value</returns> public string GenerateSignature(Uri url, string consumerKey, string consumerSecret, string token, string tokenSecret, string httpMethod, string timeStamp, string nonce, SignatureTypes signatureType, out string normalizedUrl, out string normalizedRequestParameters) { normalizedUrl = null; normalizedRequestParameters = null; switch (signatureType) { case SignatureTypes.PLAINTEXT: return HttpUtility.UrlEncode(string.Format("{0}&{1}", consumerSecret, tokenSecret)); case SignatureTypes.HMACSHA1: string signatureBase = GenerateSignatureBase(url, consumerKey, token, tokenSecret, httpMethod, timeStamp, nonce, HMACSHA1SignatureType, out normalizedUrl, out normalizedRequestParameters); HMACSHA1 hmacsha1 = new HMACSHA1(); hmacsha1.Key = Encoding.ASCII.GetBytes(string.Format("{0}&{1}", UrlEncode(consumerSecret), string.IsNullOrEmpty(tokenSecret) ? "" : UrlEncode(tokenSecret))); return GenerateSignatureUsingHash(signatureBase, hmacsha1); case SignatureTypes.RSASHA1: throw new NotImplementedException(); default: throw new ArgumentException("Unknown signature type", "signatureType"); } } /// <summary> /// Generate the timestamp for the signature /// </summary> /// <returns></returns> public virtual string GenerateTimeStamp() { // Default implementation of UNIX time of the current UTC time TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds).ToString(); } /// <summary> /// Generate a nonce /// </summary> /// <returns></returns> public virtual string GenerateNonce() { // Just a simple implementation of a random number between 123400 and 9999999 return random.Next(123400, 9999999).ToString(); } } }
its code is :
6. Add a web page to website./* * FILE: WindowsLiveLogin.cs * * DESCRIPTION: Sample implementation of Web Authentication and Delegated * Authentication protocol in C#. Also includes trusted * sign-in and application verification sample * implementations. * * VERSION: 1.1 * * Copyright (c) 2008 Microsoft Corporation. All Rights Reserved. */ using System; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using System.Collections.Specialized; using System.Collections; using System.Web; using System.Web.Configuration; using System.Security.Cryptography; using System.IO; using System.Net; using System.Reflection; using System.Xml; namespace WindowsLive { /// <summary> /// Sample implementation of Web Authentication and Delegated Authentication /// protocol. Also includes trusted sign-in and application /// verification sample implementations. /// </summary> public class WindowsLiveLogin { /// <summary> /// Stub implementation for logging debug output. You can run /// a tool such as 'dbmon' to see the output. /// </summary> static void debug(string msg) { System.Diagnostics.Debug.WriteLine(msg); System.Diagnostics.Debug.Flush(); } /// <summary> /// Initialize the WindowsLiveLogin module with the /// application ID and secret key. /// /// We recommend that you employ strong measures to protect /// the secret key. The secret key should never be /// exposed to the Web or other users. /// </summary> public WindowsLiveLogin(string appId, string secret) : this(appId, secret, null){} /// <summary> /// Initialize the WindowsLiveLogin module with the /// application ID, secret key, and security algorithm. /// /// We recommend that you employ strong measures to protect /// the secret key. The secret key should never be /// exposed to the Web or other users. /// </summary> public WindowsLiveLogin(string appId, string secret, string securityAlgorithm) : this(appId, secret, securityAlgorithm, false){} /// <summary> /// Initialize the WindowsLiveLogin module with the /// forceDelAuthNonProvisioned flag, policy URL, and return URL. /// /// The 'force_delauth_nonprovisioned' flag indicates whether /// your application is registered for Delegated Authentication /// (that is, whether it uses an application ID and secret key). We /// recommend that your Delegated Authentication application always /// be registered for enhanced security and functionality. /// </summary> public WindowsLiveLogin(bool forceDelAuthNonProvisioned, string policyUrl, string returnUrl) { ForceDelAuthNonProvisioned = forceDelAuthNonProvisioned; PolicyUrl = policyUrl; ReturnUrl = returnUrl; } /// <summary> /// Initialize the WindowsLiveLogin module with the /// application ID, secret key, security algorithm and /// forceDelAuthNonProvisioned flag. /// /// We recommend that you employ strong measures to protect /// the secret key. The secret key should never be /// exposed to the Web or other users. /// /// The 'force_delauth_nonprovisioned' flag indicates whether /// your application is registered for Delegated Authentication /// (that is, whether it uses an application ID and secret key). We /// recommend that your Delegated Authentication application always /// be registered for enhanced security and functionality. /// </summary> public WindowsLiveLogin(string appId, string secret, string securityAlgorithm, bool forceDelAuthNonProvisioned) : this(appId, secret, securityAlgorithm, forceDelAuthNonProvisioned, null){} /// <summary> /// Initialize the WindowsLiveLogin module with the /// application ID, secret key, security algorithm, /// forceDelAuthNonProvisioned and policy URL use. /// /// We recommend that you employ strong measures to protect /// the secret key. The secret key should never be /// exposed to the Web or other users. /// /// The 'force_delauth_nonprovisioned' flag indicates whether /// your application is registered for Delegated Authentication /// (that is, whether it uses an application ID and secret key). We /// recommend that your Delegated Authentication application always /// be registered for enhanced security and functionality. /// </summary> public WindowsLiveLogin(string appId, string secret, string securityAlgorithm, bool forceDelAuthNonProvisioned, string policyUrl) : this(appId, secret, securityAlgorithm, forceDelAuthNonProvisioned, policyUrl, null){} /// <summary> /// Initialize the WindowsLiveLogin module with the /// application ID, secret key, security algorithm, /// forceDelAuthNonProvisioned, policy URL and return URL. /// /// We recommend that you employ strong measures to protect /// the secret key. The secret key should never be /// exposed to the Web or other users. /// /// The 'force_delauth_nonprovisioned' flag indicates whether /// your application is registered for Delegated Authentication /// (that is, whether it uses an application ID and secret key). We /// recommend that your Delegated Authentication application always /// be registered for enhanced security and functionality. public WindowsLiveLogin(string appId, string secret, string securityAlgorithm, bool forceDelAuthNonProvisioned, string policyUrl, string returnUrl) { ForceDelAuthNonProvisioned = forceDelAuthNonProvisioned; AppId = appId; Secret = secret; SecurityAlgorithm = securityAlgorithm; PolicyUrl = policyUrl; ReturnUrl = returnUrl; } /// <summary> /// Initialize the WindowsLiveLogin module from the /// web.config file if loadAppSettings is true. Otherwise, /// you will have to manually set the AppId, Secret and /// SecurityAlgorithm properties. /// /// In a Delegated Authentication scenario, you may also specify /// the return and privacy policy URLs to use, as shown in the /// Delegated Authentication samples. /// </summary> public WindowsLiveLogin(bool loadAppSettings) { if (!loadAppSettings) { return; } NameValueCollection appSettings = WebConfigurationManager.AppSettings; if (appSettings == null) { throw new IOException("Error: WindowsLiveLogin: Failed to load the Web application settings."); } string forceDelAuthNonProvisioned = appSettings["wll_force_delauth_nonprovisioned"]; if (!string.IsNullOrEmpty(forceDelAuthNonProvisioned) && (forceDelAuthNonProvisioned.ToLower() == "true")) { ForceDelAuthNonProvisioned = true; } else { ForceDelAuthNonProvisioned = false; } AppId = appSettings["wll_appid"]; Secret = appSettings["wll_secret"]; OldSecret = appSettings["wll_oldsecret"]; OldSecretExpiry = appSettings["wll_oldsecretexpiry"]; SecurityAlgorithm = appSettings["wll_securityalgorithm"]; PolicyUrl = appSettings["wll_policyurl"]; ReturnUrl = appSettings["wll_returnurl"]; BaseUrl = appSettings["wll_baseurl"]; SecureUrl = appSettings["wll_secureurl"]; ConsentUrl = appSettings["wll_consenturl"]; } /// <summary><![CDATA[ /// Initialize the WindowsLiveLogin module from a settings file. /// /// 'settingsFile' specifies the location of the XML settings /// file containing the application ID, secret key, an optional /// security algorithm and a privacy policy URL (required for /// Delegated Auth). The file is of the following format: /// /// <windowslivelogin> /// <appid>APPID</appid> /// <secret>SECRET</secret> /// <securityalgorithm>wsignin1.0</securityalgorithm> /// <policyurl>http://[your domain]/[your privacy policy]</policyurl> /// <returnurl>http://[your domain]/[your return url]</policyurl> /// </windowslivelogin> /// /// In a Delegated Authentication scenario, you may also specify /// 'returnurl' and 'policyurl' in the settings file. /// /// We recommend that you store the Windows Live Login settings file /// in an area on your server that cannot be accessed through /// the Internet. This file contains important confidential /// information. /// ]]></summary> public WindowsLiveLogin(string settingsFile) { NameValueCollection settings = parseSettings(settingsFile); string forceDelAuthNonProvisioned = settings["force_delauth_nonprovisioned"]; if (!string.IsNullOrEmpty(forceDelAuthNonProvisioned) && (forceDelAuthNonProvisioned.ToLower() == "true")) { ForceDelAuthNonProvisioned = true; } else { ForceDelAuthNonProvisioned = false; } AppId = settings["appid"]; Secret = settings["secret"]; OldSecret = settings["oldsecret"]; OldSecretExpiry = settings["oldsecretexpiry"]; SecurityAlgorithm = settings["securityalgorithm"]; PolicyUrl = settings["policyurl"]; ReturnUrl = settings["returnurl"]; BaseUrl = settings["baseurl"]; SecureUrl = settings["secureurl"]; ConsentUrl = settings["consenturl"]; } string appId; /// <summary> /// Gets or sets the application ID. /// </summary> public string AppId { set { if (string.IsNullOrEmpty(value)) { if (ForceDelAuthNonProvisioned) { return; } throw new ArgumentNullException("value"); } Regex re = new Regex(@"^\w+$"); if (!re.IsMatch(value)) { throw new ArgumentException("Error: AppId: Application ID must be alphanumeric: " + value); } appId = value; } get { if (string.IsNullOrEmpty(appId)) { throw new InvalidOperationException("Error: AppId: Application ID was not set. Aborting."); } return appId; } } byte[] cryptKey; byte[] signKey; /// <summary> /// Sets your secret key. Use this method if you did not specify /// a secret key at initialization. /// </summary> public string Secret { set { if (string.IsNullOrEmpty(value)) { if (ForceDelAuthNonProvisioned) { return; } throw new ArgumentNullException("value"); } if (value.Length < 16) { throw new ArgumentException("Error: Secret: Secret key is expected to be longer than 16 characters: " + value.Length); } cryptKey = derive(value, "ENCRYPTION"); signKey = derive(value, "SIGNATURE"); } get { return null; } } byte[] oldCryptKey; byte[] oldSignKey; /// <summary> /// Sets your old secret key. /// /// Use this property to set your old secret key if you are in the /// process of transitioning to a new secret key. You may need this /// property because the Windows Live ID servers can take up to /// 24 hours to propagate a new secret key after you have updated /// your application settings. /// /// If an old secret key is specified here and has not expired /// (as determined by the OldSecretExpiry setting), it will be used /// as a fallback if token decryption fails with the new secret /// key. /// </summary> public string OldSecret { set { if (string.IsNullOrEmpty(value)) { return; } if (value.Length < 16) { throw new ArgumentException("Error: OldSecret: Secret key is expected to be longer than 16 characters: " + value.Length); } oldCryptKey = derive(value, "ENCRYPTION"); oldSignKey = derive(value, "SIGNATURE"); } get { return null; } } string oldSecretExpiryString; DateTime oldSecretExpiry; /// <summary> /// Sets or gets the expiry time for your old secret key. /// /// After this time has passed, the old secret key will no longer be /// used even if token decryption fails with the new secret key. /// /// The old secret expiry time is represented as the number of seconds /// elapsed since January 1, 1970. /// </summary> public string OldSecretExpiry { set { if (string.IsNullOrEmpty(value)) { return; } oldSecretExpiryString = value; int timestampInt; try { timestampInt = Convert.ToInt32(value); } catch (Exception) { throw new ArgumentException("Error: OldSecretExpiry: Invalid timestamp: " + value); } DateTime refTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); oldSecretExpiry = refTime.AddSeconds(timestampInt); } get { return oldSecretExpiryString; } } string securityAlgorithm; /// <summary> /// Sets or gets the version of the security algorithm being used. /// </summary> public string SecurityAlgorithm { set { securityAlgorithm = value; } get { if (string.IsNullOrEmpty(securityAlgorithm)) { return "wsignin1.0"; } return securityAlgorithm; } } bool forceDelAuthNonProvisioned = false; /// <summary> /// Sets or gets a flag that indicates whether Delegated Authentication /// is non-provisioned (i.e. does not use an application ID or secret /// key). /// </summary> public bool ForceDelAuthNonProvisioned { set { forceDelAuthNonProvisioned = value; } get { return forceDelAuthNonProvisioned; } } string policyUrl; /// <summary> /// Sets or gets the privacy policy URL. /// /// Set the property for Delegated Authentication, if you did /// not provide one at initialization time. /// </summary> public string PolicyUrl { set { if (string.IsNullOrEmpty(value) && ForceDelAuthNonProvisioned) { throw new ArgumentNullException("value"); } policyUrl = value; } get { if (string.IsNullOrEmpty(policyUrl)) { debug("Warning: In the initial release of Delegated Auth, a Policy URL must be configured in the SDK for both provisioned and non-provisioned scenarios."); if (ForceDelAuthNonProvisioned) { throw new InvalidOperationException("Error: PolicyUrl: Policy URL must be set in a Delegated Auth non-provisioned scenario. Aborting."); } } return policyUrl; } } string returnUrl; /// <summary> /// Sets or gets the return URL--the URL on your site to which the consent /// service redirects users (along with the action, consent token, /// and application context) after they have successfully provided /// consent information for Delegated Authentication. /// /// This value will override the return URL specified during /// registration. /// </summary> public string ReturnUrl { set { if (string.IsNullOrEmpty(value) && ForceDelAuthNonProvisioned) { throw new ArgumentNullException("value"); } returnUrl = value; } get { if (string.IsNullOrEmpty(returnUrl) && ForceDelAuthNonProvisioned) { throw new InvalidOperationException("Error: ReturnUrl: Return URL must be specified in a delegated auth non-provisioned scenario. Aborting."); } return returnUrl; } } string baseUrl; /// <summary> /// Sets or gets the URL to use for the Windows Live Login server. /// You should not have to use or change this. Furthermore, we /// recommend that you use the Sign In control instead of /// the URL methods provided here. /// </summary> public string BaseUrl { set { baseUrl = value; } get { if (string.IsNullOrEmpty(baseUrl)) { return "http://login.live.com/"; } return baseUrl; } } string secureUrl; /// <summary> /// Sets or gets the secure (HTTPS) URL to use for the Windows Live /// Login server. You should not have to use or change this /// directly. // </summary> public string SecureUrl { set { secureUrl = value; } get { if (string.IsNullOrEmpty(secureUrl)) { return "https://login.live.com/"; } return secureUrl; } } string consentUrl; /// <summary> /// Sets or gets the URL to use for the Windows Live Consent server. You /// should not have to use or change this directly. /// </summary> public string ConsentUrl { set { consentUrl = value; } get { if (string.IsNullOrEmpty(consentUrl)) { return "https://consent.live.com/"; } return consentUrl; } } /* Methods for Web Authentication support. */ /// <summary> /// Returns the sign-in URL to use for the Windows Live Login server. /// We recommend that you use the Sign In control instead. /// </summary> /// <returns>Sign-in URL</returns> public string GetLoginUrl() { return GetLoginUrl(null); } /// <summary> /// Returns the sign-in URL to use for the Windows Live Login server. /// We recommend that you use the Sign In control instead. /// </summary> /// <param name="context">If you specify it, <paramref /// name="context"/> will be returned as-is in the sign-in /// response for site-specific use.</param> /// <returns>Sign-in URL</returns> public string GetLoginUrl(string context) { return GetLoginUrl(context, null); } /// <summary> /// Returns the sign-in URL to use for the Windows Live Login server. /// We recommend that you use the Sign In control instead. /// </summary> /// <param name="context">If you specify it, <paramref /// name="context"/> will be returned as-is in the sign-in /// response for site-specific use.</param> /// <param name="market">The language in which the sign-in page is /// displayed is configured by culture ID (For example, 'fr-fr' or /// 'en-us') specified in the 'market' parameter.</param> /// <returns>Sign-in URL</returns> public string GetLoginUrl(string context, string market) { string alg = "&alg=" + SecurityAlgorithm; context = string.IsNullOrEmpty(context) ? string.Empty : "&appctx=" + HttpUtility.UrlEncode(context); market = string.IsNullOrEmpty(market) ? string.Empty : "&mkt=" + HttpUtility.UrlEncode(market); return BaseUrl + "wlogin.srf?appid=" + AppId + alg + context + market; } /// <summary> /// Returns the sign-out URL to use for the Windows Live Login server. /// We recommend that you use the Sign In control instead. /// </summary> /// <returns>Sign-out URL</returns> public string GetLogoutUrl() { return GetLogoutUrl(null); } /// <summary> /// Returns the sign-out URL to use for the Windows Live Login server. /// We recommend that you use the Sign In control instead. /// </summary> /// <param name="market">The language in which the sign-in page is /// displayed is configured by culture ID (For example, 'fr-fr' or /// 'en-us') specified in the 'market' parameter.</param> /// <returns>Sign-out URL</returns> public string GetLogoutUrl(string market) { market = string.IsNullOrEmpty(market) ? string.Empty : "&mkt=" + HttpUtility.UrlEncode(market); return BaseUrl + "logout.srf?appid=" + AppId + market; } /// <summary> /// Holds the user information after a successful sign-in. /// </summary> public class User { public User(string timestamp, string id, string flags, string context, string token) { setTimestamp(timestamp); setId(id); setFlags(flags); setContext(context); setToken(token); } DateTime timestamp; /// <summary> /// Returns the timestamp as obtained from the SSO token. /// </summary> public DateTime Timestamp { get { return timestamp; } } /// <summary> /// Sets the Unix timestamp. /// </summary> /// <param name="timestamp"></param> private void setTimestamp(string timestamp) { if (string.IsNullOrEmpty(timestamp)) { throw new ArgumentException("Error: User: Null timestamp in token."); } int timestampInt; try { timestampInt = Convert.ToInt32(timestamp); } catch (Exception) { throw new ArgumentException("Error: User: Invalid timestamp: " + timestamp); } DateTime refTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); this.timestamp = refTime.AddSeconds(timestampInt); } string id; /// <summary> /// Returns the pairwise unique ID for the user. /// </summary> public string Id { get { return id; } } /// <summary> /// Sets the pairwise unique ID for the user. /// </summary> /// <param name="id">User id</param> private void setId(string id) { if (string.IsNullOrEmpty(id)) { throw new ArgumentException("Error: User: Null id in token."); } Regex re = new Regex(@"^\w+$"); if (!re.IsMatch(id)) { throw new ArgumentException("Error: User: Invalid id: " + id); } this.id = id; } bool usePersistentCookie; /// <summary> /// Indicates whether the application /// is expected to store the user token in a session or /// persistent cookie. /// </summary> public bool UsePersistentCookie { get { return usePersistentCookie; } } /// <summary> /// Sets the usePersistentCookie flag for the user. /// </summary> /// <param name="flags"></param> private void setFlags(string flags) { this.usePersistentCookie = false; if (!string.IsNullOrEmpty(flags)) { try { int flagsInt = Convert.ToInt32(flags); this.usePersistentCookie = ((flagsInt % 2) == 1); } catch (Exception) { throw new ArgumentException("Error: User: Invalid flags: " + flags); } } } string context; /// <summary> /// Returns the application context that was originally passed /// to the sign-in request, if any. /// </summary> public string Context { get { return context; } } /// <summary> /// Sets the the Application context. /// </summary> /// <param name="context"></param> private void setContext(string context) { this.context = context; } string token; /// <summary> /// Returns the encrypted Web Authentication token containing /// the UID. This can be cached in a cookie and the UID can be /// retrieved by calling the ProcessToken method. /// </summary> public string Token { get { return token; } } /// <summary> /// Sets the the User token. /// </summary> /// <param name="token"></param> private void setToken(string token) { this.token = token; } } /// <summary> /// Processes the sign-in response from the Windows Live Login server. /// </summary> /// /// <param name="query">Contains the preprocessed POST query /// such as that returned by HttpRequest.Form</param> /// /// <returns>The method returns a User object on successful /// sign-in; otherwise null.</returns> public User ProcessLogin(NameValueCollection query) { if (query == null) { debug("Error: ProcessLogin: Invalid query."); return null; } string action = query["action"]; if (action != "login") { debug("Warning: ProcessLogin: query action ignored: " + action); return null; } string token = query["stoken"]; string context = query["appctx"]; if (context != null) { context = HttpUtility.UrlDecode(context); } return ProcessToken(token, context); } /// <summary> /// Decodes and validates a Web Authentication token. Returns a User /// object on success. /// </summary> public User ProcessToken(string token) { return ProcessToken(token, null); } /// <summary> /// Decodes and validates a Web Authentication token. Returns a User /// object on success. If a context is passed in, it will be /// returned as the context field in the User object. /// </summary> /// <param name="token">Web Authentication token</param> /// <param name="context">If you specify it, <paramref /// name="context"/> will be returned as-is in the sign-in /// response for site-specific use.</param> /// <returns>User object</returns> public User ProcessToken(string token, string context) { if (string.IsNullOrEmpty(token)) { debug("Error: ProcessToken: Invalid token."); return null; } string stoken = DecodeAndValidateToken(token); if (string.IsNullOrEmpty(stoken)) { debug("Error: ProcessToken: Failed to decode/validate token: " + token); return null; } NameValueCollection parsedToken = parse(stoken); if (parsedToken == null || parsedToken.Count < 3) { debug("Error: ProcessToken: Failed to parse token after decoding: " + token); return null; } string appId = parsedToken["appid"]; if (appId != AppId) { debug("Error: ProcessToken: Application ID in token did not match ours: " + appId + ", " + AppId); return null; } User user = null; try { user = new User(parsedToken["ts"], parsedToken["uid"], parsedToken["flags"], context, token); } catch (Exception e) { debug("Error: ProcessToken: Contents of token considered invalid: " + e); } return user; } /// <summary> /// Returns an appropriate content type and body /// response that the application handler can return to /// signify a successful sign-out from the application. /// /// When a user signs out of Windows Live or a Windows Live /// application, a best-effort attempt is made to sign the user out /// from all other Windows Live applications the user might be signed /// in to. This is done by calling the handler page for each /// application with 'action' parameter set to 'clearcookie' in the query /// string. The application handler is then responsible for clearing /// any cookies or data associated with the sign-in. After successfully /// signing the user out, the handler should return a GIF (any /// GIF) as response to the action=clearcookie query. /// </summary> public void GetClearCookieResponse(out string type, out byte[] content) { const string gif = "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAEALAAAAAABAAEAAAIBTAA7"; type = "image/gif"; content = Convert.FromBase64String(gif); } /* Methods for Delegated Authentication support. */ /// <summary> /// Returns the consent URL to use for Delegated Authentication for /// the given comma-delimited list of offers. /// </summary> /// <param name="offers">Comma-delimited list of offers.</param> /// <returns>Consent URL</returns> public string GetConsentUrl(string offers) { return GetConsentUrl(offers, null); } /// <summary> /// Returns the consent URL to use for Delegated Authentication for /// the given comma-delimited list of offers. /// </summary> /// <param name="offers">Comma-delimited list of offers.</param> /// <param name="context">If you specify it, <paramref /// name="context"/> will be returned as-is in the consent /// response for site-specific use.</param> /// <returns>Consent URL</returns> public string GetConsentUrl(string offers, string context) { return GetConsentUrl(offers, context, null); } /// <summary> /// Returns the consent URL to use for Delegated Authentication for /// the given comma-delimited list of offers. /// </summary> /// <param name="offers">Comma-delimited list of offers.</param> /// <param name="context">If you specify it, <paramref /// name="context"/> will be returned as-is in the consent /// response for site-specific use.</param> /// <param name="ru">The registered/configured return URL will be /// overridden by 'ru' specified here.</param> /// <returns>Consent URL</returns> public string GetConsentUrl(string offers, string context, string ru) { return GetConsentUrl(offers, context, ru, null); } /// <summary> /// Returns the consent URL to use for Delegated Authentication for /// the given comma-delimited list of offers. /// </summary> /// <param name="offers">Comma-delimited list of offers.</param> /// <param name="context">If you specify it, <paramref /// name="context"/> will be returned as-is in the sign-in /// response for site-specific use.</param> /// <param name="ru">The registered/configured return URL will be /// overridden by 'ru' specified here.</param> /// <param name="market">The language in which the consent page is /// displayed is configured by culture ID (For example, 'fr-fr' or /// 'en-us') specified in the 'market' parameter.</param> /// <returns>Consent URL</returns> public string GetConsentUrl(string offers, string context, string ru, string market) { if (string.IsNullOrEmpty(offers)) { throw new ArgumentException("Error: GetConsentUrl: Invalid offers list."); } offers = "?ps=" + HttpUtility.UrlEncode(offers); context = string.IsNullOrEmpty(context) ? string.Empty : "&appctx=" + HttpUtility.UrlEncode(context); if (string.IsNullOrEmpty(ru)) { ru = ReturnUrl; } ru = string.IsNullOrEmpty(ru) ? string.Empty : "&ru=" + HttpUtility.UrlEncode(ru); market = string.IsNullOrEmpty(market) ? string.Empty : "&mkt=" + HttpUtility.UrlEncode(market); string pu = string.Empty; if (!string.IsNullOrEmpty(PolicyUrl)) { pu = "&pl=" + HttpUtility.UrlEncode(PolicyUrl); } string app = string.Empty; if (!ForceDelAuthNonProvisioned) { app = "&app=" + GetAppVerifier(); } return (ConsentUrl + "Delegation.aspx" + offers + context + ru + pu + market + app); } /// <summary> /// Returns the URL to use to download a new consent token, given the /// offers and refresh token. /// </summary> /// <param name="offers">Comma-delimited list of offers.</param> /// <param name="refreshToken">Refresh token.</param> /// <returns>Refresh consent token URL</returns> public string GetRefreshConsentTokenUrl(string offers, string refreshToken) { return GetRefreshConsentTokenUrl(offers, refreshToken, null); } /// <summary> /// Returns the URL to use to download a new consent token, given the /// offers and refresh token. /// </summary> /// <param name="offers">Comma-delimited list of offers.</param> /// <param name="refreshToken">Refresh token.</param> /// <returns>Refresh consent token URL</returns> /// <param name="ru">The registered/configured return URL will be /// overridden by 'ru' specified here.</param> /// <returns>Refresh consent token URL</returns> public string GetRefreshConsentTokenUrl(string offers, string refreshToken, string ru) { if (string.IsNullOrEmpty(offers)) { throw new ArgumentException("Error: GetRefreshConsentTokenUrl: Invalid offers list."); } offers = "?ps=" + HttpUtility.UrlEncode(offers); if (string.IsNullOrEmpty(refreshToken)) { throw new ArgumentException("Error: GetRefreshConsentTokenUrl: Invalid refresh token."); } refreshToken = "&reft=" + refreshToken; if (string.IsNullOrEmpty(ru)) { ru = ReturnUrl; } ru = string.IsNullOrEmpty(ru) ? string.Empty : "&ru=" + HttpUtility.UrlEncode(ru); string app = string.Empty; if (!ForceDelAuthNonProvisioned) { app = "&app=" + GetAppVerifier(); } return ConsentUrl + "RefreshToken.aspx" + offers + refreshToken + ru + app; } /// <summary> /// Returns the URL for the consent-management user interface. /// </summary> /// <returns>Manage consent URL</returns> public string GetManageConsentUrl() { return GetManageConsentUrl(null); } /// <summary> /// Returns the URL for the consent-management user interface. /// </summary> /// <param name="market">The language in which the consent page is /// displayed is configured by culture ID (For example, 'fr-fr' or /// 'en-us') specified in the 'market' parameter.</param> /// <returns>Manage consent URL</returns> public string GetManageConsentUrl(string market) { market = string.IsNullOrEmpty(market) ? string.Empty : "?mkt=" + HttpUtility.UrlEncode(market); return ConsentUrl + "ManageConsent.aspx" + market; } /// <summary> /// Holds the Consent Token object corresponding to consent granted. /// </summary> public class ConsentToken { WindowsLiveLogin wll; /// <summary> /// Initialize the ConsentToken. /// </summary> /// <param name="wll">WindowsLiveLogin</param> /// <param name="delegationToken">Delegation token</param> /// <param name="refreshToken">Refresh token</param> /// <param name="sessionKey">Session key</param> /// <param name="expiry">Expiry</param> /// <param name="offers">Offers</param> /// <param name="locationID">Location ID</param> /// <param name="context">Application context</param> /// <param name="decodedToken">Decoded token</param> /// <param name="token">Raw token</param> public ConsentToken(WindowsLiveLogin wll, string delegationToken, string refreshToken, string sessionKey, string expiry, string offers, string locationID, string context, string decodedToken, string token) { this.wll = wll; setDelegationToken(delegationToken); setRefreshToken(refreshToken); setSessionKey(sessionKey); setExpiry(expiry); setOffers(offers); setLocationID(locationID); setContext(context); setDecodedToken(decodedToken); setToken(token); } string delegationToken; /// <summary> /// Gets the Delegation token. /// </summary> public string DelegationToken { get { return delegationToken; } } /// <summary> /// Sets the Delegation token. /// </summary> /// <param name="delegationToken">Delegation token</param> private void setDelegationToken(string delegationToken) { if (string.IsNullOrEmpty(delegationToken)) { throw new ArgumentException("Error: ConsentToken: Null delegation token."); } this.delegationToken = delegationToken; } string refreshToken; /// <summary> /// Gets the refresh token. /// </summary> public string RefreshToken { get { return refreshToken; } } /// <summary> /// Sets the refresh token. /// </summary> /// <param name="refreshToken">Refresh token</param> private void setRefreshToken(string refreshToken) { this.refreshToken = refreshToken; } byte[] sessionKey; /// <summary> /// Gets the session key. /// </summary> public byte[] SessionKey { get { return sessionKey; } } /// <summary> /// Sets the session key. /// </summary> /// <param name="sessionKey">Session key</param> private void setSessionKey(string sessionKey) { if (string.IsNullOrEmpty(sessionKey)) { throw new ArgumentException("Error: ConsentToken: Null session key."); } this.sessionKey = WindowsLiveLogin.u64(sessionKey); } DateTime expiry; /// <summary> /// Gets the expiry time of delegation token. /// </summary> public DateTime Expiry { get { return expiry; } } /// <summary> /// Sets the expiry time of delegation token. /// </summary> /// <param name="expiry">Expiry time</param> private void setExpiry(string expiry) { if (string.IsNullOrEmpty(expiry)) { throw new ArgumentException("Error: ConsentToken: Null expiry time."); } int expiryInt; try { expiryInt = Convert.ToInt32(expiry); } catch (Exception) { throw new ArgumentException("Error: Consent: Invalid expiry time: " + expiry); } DateTime refTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); this.expiry = refTime.AddSeconds(expiryInt); } IList offers; /// <summary> /// Gets the list of offers/actions for which the user granted consent. /// </summary> public IList Offers { get { return offers; } } string offersString; /// <summary> /// Gets the string representation of all the offers/actions for which /// the user granted consent. /// </summary> public String OffersString { get { return offersString; } } /// <summary> /// Sets the offers/actions for which user granted consent. /// </summary> /// <param name="offers">Comma-delimited list of offers</param> private void setOffers(string offers) { if (string.IsNullOrEmpty(offers)) { throw new ArgumentException("Error: ConsentToken: Null offers."); } offers = HttpUtility.UrlDecode(offers); this.offersString = string.Empty; this.offers = new ArrayList(); string[] offersList = offers.Split(new Char[]{';'}); foreach (string offer in offersList) { if (!(this.offersString == string.Empty)) { this.offersString += ","; } int separator = offer.IndexOf(':'); if (separator == -1) { debug("Warning: ConsentToken: offer may be invalid: " + offer); this.offers.Add(offer); this.offersString += offer; } else { string o = offer.Substring(0, separator); this.offers.Add(o); this.offersString += o; } } } string locationID; /// <summary> /// Gets the location ID. /// </summary> public string LocationID { get { return locationID; } } /// <summary> /// Sets the location ID. /// </summary> /// <param name="locationID">Location ID</param> private void setLocationID(string locationID) { this.locationID = locationID; } string context; /// <summary> /// Returns the application context that was originally passed /// to the consent request, if any. /// </summary> public string Context { get { return context; } } /// <summary> /// Sets the application context. /// </summary> /// <param name="context">Application context</param> private void setContext(string context) { this.context = context; } string decodedToken; /// <summary> /// Gets the decoded token. /// </summary> public string DecodedToken { get { return decodedToken; } } /// <summary> /// Sets the decoded token. /// </summary> /// <param name="decodedToken">Decoded token</param> private void setDecodedToken(string decodedToken) { this.decodedToken = decodedToken; } string token; /// <summary> /// Gets the raw token. /// </summary> public string Token { get { return token; } } /// <summary> /// Sets the raw token. /// </summary> /// <param name="token">Raw token</param> private void setToken(string token) { this.token = token; } /// <summary> /// Indicates whether the delegation token is set and has not expired. /// </summary> /// <returns></returns> public bool IsValid() { if (string.IsNullOrEmpty(DelegationToken)) { return false; } if (DateTime.UtcNow.AddSeconds(-300) > Expiry) { return false; } return true; } /// <summary> /// Attempt to refresh the current token and replace it. If operation succeeds /// true is returned to signify success. /// </summary> /// <returns></returns> public bool Refresh() { ConsentToken ct = wll.RefreshConsentToken(this); if (ct == null) { return false; } copy(ct); return true; } /// <summary> /// Makes a copy of the ConsentToken object. /// </summary> /// <param name="consentToken"></param> void copy(ConsentToken consentToken) { this.delegationToken = consentToken.delegationToken; this.refreshToken = consentToken.refreshToken; this.sessionKey = consentToken.sessionKey; this.expiry = consentToken.expiry; this.offers = consentToken.offers; this.locationID = consentToken.locationID; this.offersString = consentToken.offersString; this.decodedToken = consentToken.decodedToken; this.token = consentToken.token; } } /// <summary> /// Processes the POST response from the Delegated Authentication /// service after a user has granted consent. The processConsent /// function extracts the consent token string and returns the result /// of invoking the processConsentToken method. /// </summary> /// <param name="query">Response from the Delegated Authentication service.</param> /// <returns>ConsentToken</returns> public ConsentToken ProcessConsent(NameValueCollection query) { if (query == null) { debug("Error: ProcessConsent: Invalid query."); return null; } string action = query["action"]; if (action != "delauth") { debug("Warning: ProcessConsent: query action ignored: " + action); return null; } if (query["ResponseCode"] != "RequestApproved") { debug("Error: ProcessConsent: Consent was not successfully granted: " + query["ResponseCode"]); return null; } string token = query["ConsentToken"]; string context = query["appctx"]; if (!string.IsNullOrEmpty(context)) { context = HttpUtility.UrlDecode(context); } return ProcessConsentToken(token, context); } /// <summary> /// Processes the consent token string that is returned in the POST /// response by the Delegated Authentication service after a /// user has granted consent. /// </summary> /// <param name="token">Raw token.</param> /// <returns>ConsentToken</returns> public ConsentToken ProcessConsentToken(string token) { return ProcessConsentToken(token, null); } /// <summary> /// Processes the consent token string that is returned in the POST /// response by the Delegated Authentication service after a /// user has granted consent. /// </summary> /// <param name="token">Raw token.</param> /// <param name="context">If you specify it, <paramref /// name="context"/> will be returned as-is in the sign-in /// response for site-specific use.</param> /// <returns></returns> public ConsentToken ProcessConsentToken(string token, string context) { string decodedToken = token; if (string.IsNullOrEmpty(token)) { debug("Error: ProcessConsentToken: Null token."); return null; } NameValueCollection parsedToken = parse(HttpUtility.UrlDecode(token)); if (!string.IsNullOrEmpty(parsedToken["eact"])) { decodedToken = DecodeAndValidateToken(parsedToken["eact"]); if (string.IsNullOrEmpty(decodedToken)) { debug("Error: ProcessConsentToken: Failed to decode/validate token: " + token); return null; } parsedToken = parse(decodedToken); decodedToken = HttpUtility.UrlEncode(decodedToken); } ConsentToken consentToken = null; try { consentToken = new ConsentToken(this, parsedToken["delt"], parsedToken["reft"], parsedToken["skey"], parsedToken["exp"], parsedToken["offer"], parsedToken["lid"], context, decodedToken, token); } catch (Exception e) { debug("Error: ProcessConsentToken: Contents of token considered invalid: " + e); } return consentToken; } /// <summary> /// Attempts to obtain a new, refreshed token and return it. The /// original token is not modified. /// </summary> /// <param name="token">ConsentToken object.</param> /// <returns>Refreshed ConsentToken object.</returns> public ConsentToken RefreshConsentToken(ConsentToken token) { return RefreshConsentToken(token, null); } /// <summary> /// Attempts to obtain a new, refreshed token and return it. The /// original token is not modified. /// </summary> /// <param name="token">ConsentToken object.</param> /// <param name="ru">The registered/configured return URL will be /// overridden by 'ru' specified here.</param> /// <returns>Refreshed ConsentToken object.</returns> public ConsentToken RefreshConsentToken(ConsentToken token, string ru) { if (token == null) { debug("Error: RefreshConsentToken: Null consent token."); return null; } return RefreshConsentToken(token.OffersString, token.RefreshToken, ru); } /// <summary> /// Attempts to obtain a new, refreshed token and return it using /// the offers and refresh token. The original token is not modified. /// </summary> /// <param name="offers">Comma-delimited list of offers.</param> /// <param name="refreshToken">Refresh token.</param> /// <returns>Refreshed ConsentToken object.</returns> public ConsentToken RefreshConsentToken(string offers, string refreshToken) { return RefreshConsentToken(offers, refreshToken, null); } /// <summary> /// Attempts to obtain a new, refreshed token and return it using /// the offers and refresh token. The original token is not modified. /// </summary> /// <param name="offers">Comma-delimited list of offers.</param> /// <param name="refreshToken">Refresh token.</param> /// <param name="ru">The registered/configured return URL will be /// overridden by 'ru' specified here.</param> /// <returns>Refreshed ConsentToken object.</returns> public ConsentToken RefreshConsentToken(string offers, string refreshToken, string ru) { string url = null; try { url = GetRefreshConsentTokenUrl(offers, refreshToken, ru); } catch (Exception e) { debug("Error: Failed to construct refresh consent token URL: " + e); return null; } if (string.IsNullOrEmpty(url)) { debug("Error: Failed to construct refresh consent token URL."); return null; } string body = fetch(url); if (string.IsNullOrEmpty(body)) { debug("Error: RefreshConsentToken: Failed to download token."); return null; } Regex re = new Regex("{\"ConsentToken\":\"(.*)\"}"); GroupCollection gc = re.Match(body).Groups; if (gc.Count != 2) { debug("Error: RefreshConsentToken: Failed to extract token: " + body); return null; } CaptureCollection cc = gc[1].Captures; if (cc.Count != 1) { debug("Error: RefreshConsentToken: Failed to extract token: " + body); return null; } return ProcessConsentToken(cc[0].ToString()); } /* Common methods. */ /// <summary> /// Decodes and validates the raw token. /// </summary> /// <param name="token"></param> /// <returns></returns> public string DecodeAndValidateToken(string token) { bool haveOldSecret = false; if ((oldSecretExpiry != null) && (DateTime.UtcNow < oldSecretExpiry)) { if ((oldCryptKey != null) && (oldSignKey != null)) { haveOldSecret = true; } } string stoken = DecodeAndValidateToken(token, cryptKey, signKey); if (string.IsNullOrEmpty(stoken)) { if (haveOldSecret) { debug("Warning: Failed to validate token with current secret, attempting old secret."); return DecodeAndValidateToken(token, oldCryptKey, oldSignKey); } } return stoken; } /// <summary> /// Decodes and validates the raw token with appropriate crypt key /// and sign key. /// </summary> /// <param name="token">Raw token.</param> /// <param name="cryptKey">Crypt key.</param> /// <param name="signKey">Sign key.</param> /// <returns></returns> public string DecodeAndValidateToken(string token, byte[] cryptKey, byte[] signKey) { string stoken = DecodeToken(token, cryptKey); if (!string.IsNullOrEmpty(stoken)) { stoken = ValidateToken(stoken, signKey); } return stoken; } /// <summary> /// Decode the given token. Returns null on failure. /// </summary> /// /// <list type="number"> /// <item>First, the string is URL unescaped and base64 /// decoded.</item> /// <item>Second, the IV is extracted from the first 16 bytes /// of the string.</item> /// <item>Finally, the string is decrypted by using the /// encryption key.</item> /// </list> /// <param name="token">Raw token.</param> /// <returns>Decoded token.</returns> public string DecodeToken(string token) { return DecodeToken(token, cryptKey); } /// <summary> /// Decode the given token. Returns null on failure. /// </summary> /// /// <list type="number"> /// <item>First, the string is URL unescaped and base64 /// decoded.</item> /// <item>Second, the IV is extracted from the first 16 bytes /// of the string.</item> /// <item>Finally, the string is decrypted by using the /// encryption key.</item> /// </list> /// <param name="token">Raw token.</param> /// <param name="cryptKey">Crypt key.</param> /// <returns>Decoded token.</returns> public string DecodeToken(string token, byte[] cryptKey) { if (cryptKey == null || cryptKey.Length == 0) { throw new InvalidOperationException("Error: DecodeToken: Secret key was not set. Aborting."); } if (string.IsNullOrEmpty(token)) { debug("Error: DecodeToken: Null token input."); return null; } const int ivLength = 16; byte[] ivAndEncryptedValue = u64(token); if ((ivAndEncryptedValue == null) || (ivAndEncryptedValue.Length <= ivLength) || ((ivAndEncryptedValue.Length % ivLength) != 0)) { debug("Error: DecodeToken: Attempted to decode invalid token."); return null; } Rijndael aesAlg = null; MemoryStream memStream = null; CryptoStream cStream = null; StreamReader sReader = null; string decodedValue = null; try { aesAlg = new RijndaelManaged(); aesAlg.KeySize = 128; aesAlg.Key = cryptKey; aesAlg.Padding = PaddingMode.PKCS7; memStream = new MemoryStream(ivAndEncryptedValue); byte[] iv = new byte[ivLength]; memStream.Read(iv, 0, ivLength); aesAlg.IV = iv; cStream = new CryptoStream(memStream, aesAlg.CreateDecryptor(), CryptoStreamMode.Read); sReader = new StreamReader(cStream, Encoding.ASCII); decodedValue = sReader.ReadToEnd(); } catch (Exception e) { debug("Error: DecodeToken: Decryption failed: " + e); return null; } finally { try { if (sReader != null) { sReader.Close(); } if (cStream != null) { cStream.Close(); } if (memStream != null) { memStream.Close(); } if (aesAlg != null) { aesAlg.Clear(); } } catch (Exception e) { debug("Error: DecodeToken: Failure during resource cleanup: " + e); } } return decodedValue; } /// <summary> /// Creates a signature for the given string. /// </summary> public byte[] SignToken(string token) { return SignToken(token, signKey); } /// <summary> /// Creates a signature for the given string by using the /// signature key. /// </summary> public byte[] SignToken(string token, byte[] signKey) { if (signKey == null || signKey.Length == 0) { throw new InvalidOperationException("Error: SignToken: Secret key was not set. Aborting."); } if (string.IsNullOrEmpty(token)) { debug("Attempted to sign null token."); return null; } using (HashAlgorithm hashAlg = new HMACSHA256(signKey)) { byte[] data = Encoding.Default.GetBytes(token); byte[] hash = hashAlg.ComputeHash(data); return hash; } } /// <summary> /// Extracts the signature from the token and validates it. /// </summary> /// <param name="token"></param> /// <returns></returns> public string ValidateToken(string token) { return ValidateToken(token, signKey); } /// <summary> /// Extracts the signature from the token and validates it by using the /// signature key. /// </summary> public string ValidateToken(string token, byte[] signKey) { if (string.IsNullOrEmpty(token)) { debug("Error: ValidateToken: Null token."); return null; } string[] s = { "&sig=" }; string[] bodyAndSig = token.Split(s, StringSplitOptions.None); if (bodyAndSig.Length != 2) { debug("Error: ValidateToken: Invalid token: " + token); return null; } byte[] sig = u64(bodyAndSig[1]); if (sig == null) { debug("Error: ValidateToken: Could not extract the signature from the token."); return null; } byte[] sig2 = SignToken(bodyAndSig[0], signKey); if (sig2 == null) { debug("Error: ValidateToken: Could not generate a signature for the token."); return null; } if (sig.Length == sig2.Length) { for (int i = 0; i < sig.Length; i++) { if (sig[i] != sig2[i]) { goto badSig; } } return token; } badSig: debug("Error: ValidateToken: Signature did not match."); return null; } /* Implementation of the methods needed to perform Windows Live application verification as well as trusted sign-in. */ /// <summary> /// Generates an Application Verifier token. /// </summary> public string GetAppVerifier() { return GetAppVerifier(null); } /// <summary> /// Generates an Application Verifier token. An IP address /// can be included in the token. /// </summary> public string GetAppVerifier(string ip) { ip = string.IsNullOrEmpty(ip) ? string.Empty : ("&ip=" + ip); string token = "appid=" + AppId + "&ts=" + getTimestamp() + ip; string sig = e64(SignToken(token)); if (string.IsNullOrEmpty(sig)) { debug("Error: GetAppVerifier: Failed to sign the token."); return null; } token += "&sig=" + sig; return HttpUtility.UrlEncode(token); } /// <summary> /// Returns the URL needed to retrieve the application /// security token. The application security token /// will be generated for the Windows Live site. /// /// JavaScript Output Notation (JSON) output is returned: /// /// {"token":"<value>"} /// </summary> public string GetAppLoginUrl() { return GetAppLoginUrl(null, null, false); } /// <summary> /// Returns the URL needed to retrieve the application /// security token. /// /// By default, the application security token will be /// generated for the Windows Live site; a specific Site ID /// can optionally be specified in 'siteId'. /// /// JSON output is returned: /// /// {"token":"<value>"} /// </summary> public string GetAppLoginUrl(string siteId) { return GetAppLoginUrl(siteId, null, false); } /// <summary> /// Returns the URL needed to retrieve the application /// security token. /// /// By default, the application security token will be /// generated for the Windows Live site; a specific Site ID /// can optionally be specified in 'siteId'. The IP address /// can also optionally be included in 'ip'. /// /// JSON output is returned: /// /// {"token":"<value>"} /// </summary> public string GetAppLoginUrl(string siteId, string ip) { return GetAppLoginUrl(siteId, ip, false); } /// <summary> /// Returns the URL needed to retrieve the application /// security token. /// /// By default, the application security token will be /// generated for the Windows Live site; a specific Site ID /// can optionally be specified in 'siteId'. The IP address /// can also optionally be included in 'ip'. /// /// If 'js' is false, then JSON output is returned: /// /// {"token":"<value>"} /// /// Otherwise, a JavaScript response is returned. It is assumed /// that WLIDResultCallback is a custom function implemented to /// handle the token value: /// /// WLIDResultCallback("<tokenvalue>"); /// </summary> public string GetAppLoginUrl(string siteId, string ip, bool js) { string algPart = "&alg=" + SecurityAlgorithm; string sitePart = string.IsNullOrEmpty(siteId) ? string.Empty : "&id=" + siteId; string jsPart = (!js) ? string.Empty : "&js=1"; string url = SecureUrl + "wapplogin.srf?app=" + GetAppVerifier(ip) + algPart + sitePart + jsPart; return url; } /// <summary> /// Retrieves the application security token for application /// verification from the application sign-in URL. The /// application security token will be generated for the /// Windows Live site. /// </summary> public string GetAppSecurityToken() { return GetAppSecurityToken(null, null); } /// <summary> /// Retrieves the application security token for application /// verification from the application sign-in URL. /// /// By default, the application security token will be /// generated for the Windows Live site; a specific Site ID /// can optionally be specified in 'siteId'. /// </summary> public string GetAppSecurityToken(string siteId) { return GetAppSecurityToken(siteId, null); } /// <summary> /// Retrieves the application security token for application /// verification from the application sign-in URL. /// /// By default, the application security token will be /// generated for the Windows Live site; a specific Site ID /// can optionally be specified in 'siteId'. The IP address /// can also optionally be included in 'ip'. /// /// Implementation note: The application security token is /// downloaded from the application sign-in URL in JSON format /// {"token":"<value>"}, so we need to extract /// <value> from the string and return it as seen here. /// </summary> public string GetAppSecurityToken(string siteId, string ip) { string url = GetAppLoginUrl(siteId, ip); string body = fetch(url); if (string.IsNullOrEmpty(body)) { debug("Error: GetAppSecurityToken: Failed to download token."); return null; } Regex re = new Regex("{\"token\":\"(.*)\"}"); GroupCollection gc = re.Match(body).Groups; if (gc.Count != 2) { debug("Error: GetAppSecurityToken: Failed to extract token: " + body); return null; } CaptureCollection cc = gc[1].Captures; if (cc.Count != 1) { debug("Error: GetAppSecurityToken: Failed to extract token: " + body); return null; } return cc[0].ToString(); } /// <summary> /// Returns a string that can be passed to the GetTrustedParams /// function as the 'retcode' parameter. If this is specified as /// the 'retcode', then the app will be used as return URL /// after it finishes trusted sign-in. /// </summary> public string GetAppRetCode() { return "appid=" + AppId; } /// <summary> /// Returns a table of key-value pairs that must be posted to /// the sign-in URL for trusted sign-in. Use HTTP POST to do /// this. Be aware that the values in the table are neither /// URL nor HTML escaped and may have to be escaped if you are /// inserting them in code such as an HTML form. /// /// The user to be trusted on the local site is passed in as /// string 'user'. /// </summary> public NameValueCollection GetTrustedParams(string user) { return GetTrustedParams(user, null); } /// <summary> /// Returns a table of key-value pairs that must be posted to /// the sign-in URL for trusted sign-in. Use HTTP POST to do /// this. Be aware that the values in the table are neither /// URL nor HTML escaped and may have to be escaped if you are /// inserting them in code such as an HTML form. /// /// The user to be trusted on the local site is passed in as /// string 'user'. /// /// Optionally, 'retcode' specifies the resource to which /// successful sign-in is redirected, such as Windows Live Mail, /// and is typically a string in the format 'id=2000'. If you /// pass in the value from GetAppRetCode instead, sign-in will /// be redirected to the application. Otherwise, an HTTP 200 /// response is returned. /// </summary> public NameValueCollection GetTrustedParams(string user, string retcode) { string token = GetTrustedToken(user); if (string.IsNullOrEmpty(token)) { return null; } token = "<wst:RequestSecurityTokenResponse xmlns:wst=\"http://schemas.xmlsoap.org/ws/2005/02/trust\"><wst:RequestedSecurityToken><wsse:BinarySecurityToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">" + token + "</wsse:BinarySecurityToken></wst:RequestedSecurityToken><wsp:AppliesTo xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\"><wsa:EndpointReference xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\"><wsa:Address>uri:WindowsLiveID</wsa:Address></wsa:EndpointReference></wsp:AppliesTo></wst:RequestSecurityTokenResponse>"; NameValueCollection nvc = new NameValueCollection(3); nvc["wa"] = SecurityAlgorithm; nvc["wresult"] = token; if (retcode != null) { nvc["wctx"] = retcode; } return nvc; } /// <summary> /// Returns the trusted sign-in token in the format needed by the /// trusted sign-in gadget. /// /// User to be trusted on the local site is passed in as string /// 'user'. /// </summary> public string GetTrustedToken(string user) { if (string.IsNullOrEmpty(user)) { debug("Error: GetTrustedToken: Invalid user specified."); return null; } string token = "appid=" + AppId + "&uid=" + HttpUtility.UrlEncode(user) + "&ts=" + getTimestamp(); string sig = e64(SignToken(token)); if (string.IsNullOrEmpty(sig)) { debug("Error: GetTrustedToken: Failed to sign the token."); return null; } token += "&sig=" + sig; return HttpUtility.UrlEncode(token); } /// <summary> /// Returns the trusted sign-in URL to use for the Windows Live /// Login server. /// </summary> public string GetTrustedLoginUrl() { return SecureUrl + "wlogin.srf"; } /// <summary> /// Returns the trusted sign-out URL to use for the Windows Live /// Login server. /// </summary> public string GetTrustedLogoutUrl() { return SecureUrl + "logout.srf?appid=" + AppId; } /* Helper methods */ /// <summary> /// Function to parse the settings file. /// </summary> /// <param name="settingsFile"></param> /// <returns></returns> static NameValueCollection parseSettings(string settingsFile) { if (string.IsNullOrEmpty(settingsFile)) { throw new ArgumentNullException("settingsFile"); } // Throws an exception on any failure. XmlDocument xd = new XmlDocument(); xd.Load(settingsFile); XmlNode topNode = xd.SelectSingleNode("//windowslivelogin"); if (topNode == null) { throw new XmlException("Error: parseSettings: Failed to parse settings file: " + settingsFile); } NameValueCollection settings = new NameValueCollection(); IEnumerator children = topNode.GetEnumerator(); while (children.MoveNext()) { XmlNode child = (XmlNode) children.Current; settings[child.Name] = child.InnerText; } return settings; } /// <summary> /// Derives the key, given the secret key and prefix as described in the /// Web Authentication SDK documentation. /// </summary> static byte[] derive(string secret, string prefix) { using (HashAlgorithm hashAlg = HashAlgorithm.Create("SHA256")) { const int keyLength = 16; byte[] data = Encoding.Default.GetBytes(prefix+secret); byte[] hashOutput = hashAlg.ComputeHash(data); byte[] byteKey = new byte[keyLength]; Array.Copy(hashOutput, byteKey, keyLength); return byteKey; } } /// <summary> /// Parses query string and return a table representation of /// the key and value pairs. Similar to /// HttpUtility.ParseQueryString, except that no URL decoding /// is done and only the last value is considered in the case /// of multiple values with one key. /// </summary> static NameValueCollection parse(string input) { if (string.IsNullOrEmpty(input)) { debug("Error: parse: Null input."); return null; } NameValueCollection pairs = new NameValueCollection(); string[] kvs = input.Split(new Char[]{'&'}); foreach (string kv in kvs) { int separator = kv.IndexOf('='); if ((separator == -1) || (separator == kv.Length)) { debug("Warning: parse: Ignoring pair: " + kv); continue; } pairs[kv.Substring(0, separator)] = kv.Substring(separator+1); } return pairs; } /// <summary> /// Generates a timestamp suitable for the application /// verifier token. /// </summary> static string getTimestamp() { DateTime refTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); TimeSpan ts = DateTime.UtcNow - refTime; return ((uint)ts.TotalSeconds).ToString(); } /// <summary> /// Base64-encodes and URL-escapes a byte array. /// </summary> static string e64(byte[] b) { string s = null; if (b == null) { return s; } try { s = Convert.ToBase64String(b); s = HttpUtility.UrlEncode(s); } catch(Exception e) { debug("Error: e64: Base64 conversion error: " + e); } return s; } /// <summary> /// URL-unescapes and Base64-decodes a string. /// </summary> static byte[] u64(string s) { byte[] b = null; if (s == null) { return b; } s = HttpUtility.UrlDecode(s); try { b = Convert.FromBase64String(s); } catch (Exception e) { debug("Error: u64: Base64 conversion error: " + s + ", " + e); } return b; } /// <summary> /// Fetches the contents given a URL. /// </summary> static string fetch(string url) { string body = null; try { WebRequest req = HttpWebRequest.Create(url); req.Method = "GET"; WebResponse res = req.GetResponse(); using (StreamReader sr = new StreamReader(res.GetResponseStream(), Encoding.UTF8)) { body = sr.ReadToEnd(); } } catch (Exception e) { debug("Error: fetch: Failed to get the document: " + url + ", " + e); } return body; } } }
HTML code is :
Preview (hint: you can copy and paste the preview into Microsoft Word):
<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="ImportContacts._Default" %> <asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent"> <script src="Scripts/jquery-1.4.1.js" type="text/javascript"></script> <script type="text/javascript"> function GetGmailContacts() { window.location = 'https://accounts.google.com/o/oauth2/auth?client_id=569316988455.apps.googleusercontent.com&redirect_uri=http://test.ie&scope=https://www.google.com/m8/feeds/&response_type=code' } function yahooclick() { $.ajax({ type: "POST", url: "Default.aspx/GetYahooContacts", contentType: "application/json", success: function (data) { // Replace the div's content with the page method's return. window.location = data.d; } }); } function hotmailclick() { $.ajax({ type: "POST", url: "Default.aspx/GetHotmailContacts", contentType: "application/json", success: function (data) { // Replace the div's content with the page method's return. window.location = data.d; } }); } </script> </asp:Content> <asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent"> <img src="Images/yahoo_mail_icon.png" alt="" onclick="yahooclick();" /> <img src="Images/wave4hotmail.png" alt="" onclick="hotmailclick();" /> <img src="Images/Gmail_logo.png" alt="" onclick="GetGmailContacts();" /> <div> <h2>Emails:</h2> <asp:ListView runat="server" ID="listView" > <ItemTemplate> <span><%# Container.DataItem %></span><br /> </ItemTemplate> </asp:ListView> </div> </asp:Content> |
and code behind code is:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.Services; using System.Configuration; using WindowsLive; using System.Net; using System.Xml; using System.IO; using Google.GData.Client; using Google.Contacts; using System.Web.Script.Serialization; namespace ImportContacts { public partial class _Default : System.Web.UI.Page { const string Offers = "Contacts.View"; const string AuthCookie = "delauthtoken"; static WindowsLiveLogin wll = new WindowsLiveLogin(true); protected WindowsLiveLogin.ConsentToken Token; protected string ConsentUrl; protected void Page_Load(object sender, EventArgs e) { ConsentUrl = wll.GetConsentUrl(Offers); Session["wll"] = wll; if (Session["HotmailToken"] != null) { WindowsLiveLogin.ConsentToken token = wll.ProcessConsent(Request.Form); Token = wll.ProcessConsentToken((string)Session["HotmailToken"]); if (Token != null) { string lid = Token.LocationID; long llid = Int64.Parse(lid, System.Globalization.NumberStyles.HexNumber); string delegatedToken = Token.DelegationToken; string uri = "https://livecontacts.services.live.com/@L@" + lid + "/rest/LiveContacts/Contacts/"; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.UserAgent = "Windows Live Data Interactive SDK"; request.ContentType = "application/xml; charset=utf-8"; request.Method = "GET"; request.Headers.Add("Authorization", "DelegatedToken dt=\"" + delegatedToken + "\""); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); XmlDocument contacts = new XmlDocument(); contacts.LoadXml(new StreamReader(response.GetResponseStream()).ReadToEnd()); List<string> emails = new List<string>(); XmlNodeList nodeList = contacts.SelectNodes("//Email/Address"); foreach (XmlNode node in nodeList) { if (!String.IsNullOrEmpty(node.InnerText)) { emails.Add(node.InnerText); } } listView.DataSource = emails; listView.DataBind(); } } if (Request["code"] != null) { List<string> emails = GetGmailContacts((string)Request["code"]); listView.DataSource = emails; listView.DataBind(); } //yahoo if (Request["oauth_token"] != null) { Yahoo.GetYahooContacts yahoo = (Yahoo.GetYahooContacts)HttpContext.Current.Session["yahoo"]; yahoo.OauthToken = Request["oauth_token"]; yahoo.OauthVerifier = Request["oauth_verifier"]; List<string> emails = yahoo.GetContacts(); listView.DataSource = emails; listView.DataBind(); } } [WebMethod] public static string GetYahooContacts() { Yahoo.GetYahooContacts yahoo = new Yahoo.GetYahooContacts(); HttpContext.Current.Session["yahoo"] = yahoo; return yahoo.authorization(ConfigurationManager.AppSettings["ReturnUrl"]); } [WebMethod] public static string GetHotmailContacts() { return wll.GetConsentUrl(Offers); } public List<string> GetGmailContacts(string code) { List<string> emails = new List<string>(); try { string postcontents = string.Format("code={0}&client_id={1}&client_secret={2}&redirect_uri={3}&grant_type=authorization_code" , System.Web.HttpUtility.UrlEncode(code) , System.Web.HttpUtility.UrlEncode(ConfigurationManager.AppSettings["gmailclientid"]) , System.Web.HttpUtility.UrlEncode(ConfigurationManager.AppSettings["gmailsecret"]) , System.Web.HttpUtility.UrlEncode(ConfigurationManager.AppSettings["gmailreturnurl"])); HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://accounts.google.com/o/oauth2/token"); request.Method = "POST"; byte[] postcontentsArray = System.Text.Encoding.UTF8.GetBytes(postcontents); request.ContentType = "application/x-www-form-urlencoded"; request.ContentLength = postcontentsArray.Length; GoogleOAuthToken token; using (Stream requestStream = request.GetRequestStream()) { requestStream.Write(postcontentsArray, 0, postcontentsArray.Length); requestStream.Close(); WebResponse response = request.GetResponse(); using (Stream responseStream = response.GetResponseStream()) using (StreamReader reader = new StreamReader(responseStream)) { string responseFromServer = reader.ReadToEnd(); reader.Close(); responseStream.Close(); response.Close(); // return SerializeToken(responseFromServer); JavaScriptSerializer ser = new JavaScriptSerializer(); token = ser.Deserialize<GoogleOAuthToken>(responseFromServer); } } RequestSettings contactrequest = new RequestSettings("import contact", token.access_token); contactrequest.AutoPaging = true; ContactsRequest req = new ContactsRequest(contactrequest); foreach (Contact contact in req.GetContacts().Entries) { emails.Add(contact.PrimaryEmail.Address); } return emails; } catch (Exception ex) { return emails; } } public class GoogleOAuthToken { public string access_token; public string expires_in; public string token_type; public string refresh_token; } } }
And there will be some settings in web.config also :
thats all<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0"/> </system.web> <system.web.extensions> <scripting> <scriptResourceHandler enableCompression="true" enableCaching="true" /> </scripting> </system.web.extensions> <appSettings> <add key="wll_appid" value="******" /> <add key="wll_secret" value="******************" /> <add key="wll_securityalgorithm" value="wsignin1.0" /> <add key="wll_returnurl" value="http://localhost:4730/default.aspx" /> <add key="wll_policyurl" value="http://test.ie/policy.html" /> <add key="ReturnUrl" value="http://localhost:4730/" /> <add key="gmailclientid" value="************************" /> <add key="gmailsecret" value="************************" /> <add key="gmailreturnurl" value="http://localhost:4730" /> </appSettings> </configuration>
can u please send me poc on my gmail id pjitendra36@gmail.com
ReplyDelete