I want to create a registrant for a webinar using GoToWebinar API's. I came across the code at gotowebinar api php
I provided my username and password to get the oAuth object. This worked perfectly fine as described.
Now I want to do something like this:
I have a Registration page. When user fills in the required details, selects the 'register to webinar' option and clicks on 'Submit', I want to enrol him for that webinar using CreateRegistrant API. The problem is, I am not able to get the oAuth object without providing username and password. Is there a way to pass this programatically and create oAuth object?
I store my API key, UserID and password in my WebConfig then read them into a Login Object for use when I do authorization. Here's how I do it in C#:
public class Login
{
public string UserId
{ get { return System.Configuration.ConfigurationManager.AppSettings["GTWUserId"]; } }
public string Password
{ get { return System.Configuration.ConfigurationManager.AppSettings["GTWPassword"]; } }
public string APIKey
{ get { return System.Configuration.ConfigurationManager.AppSettings["GTWAPIKey"]; } }
}
public string DoAuthorize()
{
Login lg = new Login();
string sError = "";
// first we need to create the uri for the web request
string uri = String.Format("https://api.citrixonline.com/oauth/access_token?grant_type=password&user_id={0}&password={1}&client_id={2}",
lg.UserId, lg.Password, lg.APIKey);
// then the request to login is created and sent. From the response
// we need to store at least the access token and the organizer key
// to use for further calls
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
request.Accept = "application/json";
request.ContentType = "application/json";
try
{
var response = request.GetResponse();
//the following lines duplicate the response stream so we can read it for
//deserialization and also re-read it and write it out.
using (MemoryStream ms = new MemoryStream())
{
var stream = response.GetResponseStream();
stream.CopyTo(ms);
ms.Position = 0;
stream.Close();
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ResponseDirectLogin));
var deserialized = (ResponseDirectLogin)ser.ReadObject(ms);
auth.OauthToken = deserialized.AccessToken;
auth.OrganizerKey = deserialized.OrganizerKey;
}
}
catch (WebException e)
{
using (var sr = new StreamReader(e.Response.GetResponseStream()))
sError = sr.ReadToEnd();
sError = String.Concat(sError, "/n", uri);
}
return sError;
}
public class Auth {
public string OauthToken { get; set; }
public string OrganizerKey { get; set; }
}
public static Auth auth = new Auth(); // This is actually in a BaseControlelr inherited by our MVC Home Controller.
public string DoRegister(string WebinarKey)
{
// Here we authorize if we haven't alerady
if (auth.OauthToken == null)
{
sMessage = DoAuthorize();
}
// first we need to create the uri for the web request
// OrganizerKey is your authorization key for the webinar organizer
string uri = String.Format(#"https://api.citrixonline.com/G2W/rest/organizers/{0}/webinars/{1}/registrants",
OrganizerKey, WebinarKey);
//then create and serialize the registrant object
// This is for when you have questions on your webinar, you can omit them if you don't have any
List<questions> q = GetQuestionKeys(Key, OrganizerKey);
List<response> responses_ = new List<response>();
foreach (var question in q)
{
response res1 = new response();
res1.questionKey = question.questionKey;
// determine which question and set the response
if (question.question == "question")
{
res1.responseText = "response";
responses_.Add(res1);
}
}
var registrant = new Registrant
{
firstName = FirstName,
lastName = LastName,
email = EmailAddress,
responses = responses_.ToArray()
};
JavaScriptSerializer ser = new JavaScriptSerializer();
string json = ser.Serialize(registrant);
// then the request to create a registrant is created and sent
// N.B. we need to include the access token to the headers to access
// the user's account and data
try {
WebClient client = new WebClient();
client.Headers = new WebHeaderCollection();
client.Headers.Add("Accept", "application/vnd.citrix.g2wapi-v1.1+json");
client.Headers.Add("Content-type", "application/json");
client.Headers.Add("Authorization", string.Format("OAuth oauth_token={0}", OAuthToken));
try
{
string resp = client.UploadString(uri, "POST", json);
var ok = ser.Deserialize<ResponseCreateRegistrantOk>(resp);
}
catch (WebException e)
{
//if there is an error, e.g. the registrant exists already
// we need an alternative deserialization
Stream s = new MemoryStream();
using (Stream response = e.Response.GetResponseStream())
{
byte[] buffer = new byte[1024];
int byteCount;
do
{
byteCount = response.Read(buffer, 0, buffer.Length);
s.Write(buffer, 0, byteCount);
} while (byteCount > 0);
}
s.Seek(0, SeekOrigin.Begin);
string content = new StreamReader(s, Encoding.UTF8).ReadToEnd();
s.Seek(0, SeekOrigin.Begin);
using (var err = new StreamReader(s))
{
var sb = new StringBuilder("Registration Error\n");
if (content.IndexOf("int_err_code") > -1)
{
var dupe = ser.Deserialize<ResponseCreateRegistrantDuplicate>(err.ReadToEnd());
sb.AppendFormat(String.Format("Error Code: {0}<br />", dupe.ErrorCode));
sb.AppendFormat(String.Format("Message: {0}<br />", dupe.Message));
}
else
{
var dupe = ser.Deserialize<ResponseCreateRegistrantDuplicate>(err.ReadToEnd());
sb.AppendFormat(String.Format("Description: {0}<br />", dupe.Description));
//sb.AppendFormat(String.Format("Incident: {0}<br />", dupe.Incident));
//sb.AppendFormat(String.Format("Registrant key: {0}<br />", dupe.RegistrantKey));
sb.AppendFormat(String.Format("Join Url: {0}<br />", dupe.JoinUrl));
}
sMessage = sb.ToString();
}
}
} catch (Exception exc) {
exc.Data.Add("stringInfo", "inside");
return "";
}
return sMessage;
}
Related
Am trying to create a an azure function that is triggered in a Logic Apps,
The functions purpose is to web crawl certain web sites, take the desired information, compare that with a a SQL Server database in Azure, compare if we already have that information if not add it.
My issue is that when ever i run it I get the Server 500 error, I assume its accessing the database that cause. Help?
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log
)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string RequestBody = await new StreamReader(req.Body).ReadToEndAsync();
{
return await CrawlBlog(0, RequestBody);
}
}
private static async Task<IActionResult> CrawlBlog(int Picker, string req)
{
int BlogPicker = Picker;
string TheResult = req;
//Get the url we want to test
var Url = "";
if (BlogPicker == 0)
{
Url = "*********";
}
else if (BlogPicker == 1)
{
Url = "*********";
}
/*
else if (BlogPicker == 2)
{
Url = "https://azure.microsoft.com/en-in/blog/?utm_source=devglan";
}
*/
else
{
TheResult = "False we got a wrong pick";
return (ActionResult)new OkObjectResult
( new {TheResult });
}
var httpClient = new HttpClient();
var html = await httpClient.GetStringAsync(Url);
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(html);
//a list to add all availabel blogs we found
var Blog = new List<BlogStats>();
switch (BlogPicker)
{
case 0:
{
var divs =
htmlDocument.DocumentNode.Descendants("div")
.Where(node => node.GetAttributeValue("class", "").Equals("home_blog_sec_text")).ToList();
foreach (var divo in divs)
{
var Blogo = new BlogStats
{
Summary = divo.Descendants("p").FirstOrDefault().InnerText,
Link = divo.Descendants("a").FirstOrDefault().ChildAttributes("href").FirstOrDefault().Value,
Title = divo.Descendants("a").FirstOrDefault().InnerText
};
Blog.Add(Blogo);
}
break;
}
case 1:
{
var divs =
htmlDocument.DocumentNode.Descendants("div")
.Where(node => node.GetAttributeValue("class", "").Equals("post_header_title two_third last")).ToList();
foreach (var divo in divs)
{
//string TheSummary = "we goofed";
var ThePs = divo.Descendants("p").ToList();
var Blogo = new BlogStats
{
Summary = ThePs[1].GetDirectInnerText(),
Link = divo.Descendants("a").LastOrDefault().ChildAttributes("href").FirstOrDefault().Value,
Title = divo.Descendants("a").FirstOrDefault().InnerText
};
Blog.Add(Blogo);
}
break;
}
}
TheResult = await SqlCheck(Blog[0].Title, Blog[0].Summary, Blog[0].Link); //error 500
return (ActionResult)new OkObjectResult
(
new
{
TheResult
}
);
}
public static async Task<string> SqlCheck(string Tit, string Sumy, string Lin)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = "flygon.database.windows.net";
builder.UserID = "*****";
builder.Password = "********";
builder.InitialCatalog = "torkoal";
System.Data.DataSet ds = new System.Data.DataSet();
SqlConnection connection = new SqlConnection(builder.ConnectionString);
connection.Open();
SqlCommand CheckCommand = new SqlCommand("SELECT * FROM TableBoto WHERE Link = #id3 ", connection);
CheckCommand.Parameters.AddWithValue("#id3", Lin);
SqlDataAdapter dataAdapter = new SqlDataAdapter(CheckCommand);
dataAdapter.Fill(ds);
int i = ds.Tables[0].Rows.Count;
if (i > 0)
{
return $" We got a Duplicates in title : {Tit}";
}
try
{
{
string query = $"insert into TableBoto(Title,Summary,Link) values('{Tit}','{Sumy}','{Lin}');";
SqlCommand command = new SqlCommand(query, connection);
SqlDataReader reader = await command.ExecuteReaderAsync();
reader.Close();
}
}
catch (SqlException)
{
// Console.WriteLine(e.ToString());
}
connection.Close();
return $" Success Ign +{Tit} + Ign {Sumy}+ Ign {Lin} Ign Success SQL ";
}
}
500 HTTP status code is a generic code which means that the server was not able to process the request due to some issues, First step would be to add some exception handling to your function and see if the failure occurs and where it occurs.
On Side note, you should not use HTTP client in the way used in the code, you should not new it up every time your function executes, this client should be static in nature. Refer Manage connections in Azure Functions
I have two interfaces for components that each requires functionality from the other one. One that generates Oauth tokens, and another one that gets secrets from a secret provider (Azure Key Vault).
The problem is that the Token Provider needs to obtain a secret value (a password) to make its HTTP call, and the Secret Provider class needs to get a Token in order to call Azure. Chicken and Egg problem.
From the other questions I've read, one suggestion is to create a third class/interface on which the original 2 depend, but I'm not sure how that would work here.
Any help and suggestions would be appreciated. Code for all relevant classes/interfaces is shown below.
public interface ISecretProvider
{
string GetSecret(string secretName);
}
public interface ITokenProvider
{
string GetKeyVaultToken();
}
public class OktaTokenProvider : ITokenProvider
{
ISecretProvider _secretProvider;
public string GetKeyVaultToken()
{
var tokenUrl = ConfigurationManager.AppSettings["KeyVault.Token.Url"];
var clientId = ConfigurationManager.AppSettings["KeyVault.Token.ClientId"];
var clientSecret = _secretProvider.GetSecret("ClientSecret");
var scope = ConfigurationManager.AppSettings["KeyVault.Scope"];
var token = GetToken(tokenUrl, clientId, clientSecret, scope);
return token;
}
private string GetToken(string tokenUrl, string clientId, string clientSecret, string scope)
{
var clientCredentials = $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($"{clientId}:{clientSecret}"))}";
string responseFromServer = string.Empty;
bool success = false;
int retryCount = 0;
while (!success)
{
try
{
var tokenWebRequest = (HttpWebRequest)WebRequest.Create(tokenUrl);
tokenWebRequest.Method = "POST";
tokenWebRequest.Headers.Add($"Authorization:{clientCredentials}");
tokenWebRequest.Headers.Add("Cache-control:no-cache");
tokenWebRequest.ContentType = "application/x-www-form-urlencoded";
using (var streamWriter = new StreamWriter(tokenWebRequest.GetRequestStream()))
{
streamWriter.Write($"grant_type=client_credentials&scope={scope}");
streamWriter.Flush();
streamWriter.Close();
}
using (WebResponse response = tokenWebRequest.GetResponse())
{
using (var dataStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
responseFromServer = reader.ReadToEnd();
reader.Close();
}
dataStream.Close();
}
response.Close();
response.Dispose();
}
success = true;
}
catch (Exception)
{
if (retryCount > 3)
{
throw;
}
else
{
retryCount++;
}
}
}
JToken token = JObject.Parse(responseFromServer);
var accessToken = $"Bearer {token.SelectToken("access_token").ToString()}";
return accessToken;
}
}
public class KeyVaultSecretProvider : ISecretProvider
{
ITokenProvider _tokenProvider;
public KeyVaultSecretProvider(ITokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
public string GetSecret(string secretName)
{
var KeyVaultUrl = ConfigurationManager.AppSettings[Constants.KEYVAULT_ENDPOINT];
var subscriptionKey = ConfigurationManager.AppSettings[Constants.KEYVAULT_SUBSCRIPTION_KEY];
string responseFromServer = "";
var requestedSecretUrl = $"{KeyVaultUrl}{secretName}";
var secretWebRequest = (HttpWebRequest)WebRequest.Create(requestedSecretUrl);
var accessToken = _tokenProvider.GetKeyVaultToken();
secretWebRequest.Method = "GET";
secretWebRequest.Headers.Add("authorization:" + accessToken);
secretWebRequest.Headers.Add("cache-control:no-cache");
secretWebRequest.Headers.Add("Ocp-Apim-Subscription-Key:" + subscriptionKey);
using (WebResponse response = secretWebRequest.GetResponse())
{
using (var dataStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
responseFromServer = reader.ReadToEnd();
reader.Close();
}
dataStream.Close();
}
response.Close();
response.Dispose();
}
JToken secret = JObject.Parse(responseFromServer);
var secretValue = secret.SelectToken("Secret").ToString();
return secretValue;
}
}
Have a single class implement both interfaces. The two responsibilities are inter-dependent, so put them together in one class. There is nothing wrong with this.
We are trying to implement Twitter new DM API from Salesforce. We are sending our the JSON request in the body as mentioned in documentation but the legacy method for Oauth authorization is not working. Any help is greatly appreciated.
To add, I am sending a DM from salesforce to twitter, So
1) I am setting the request body in JSON.
2) I am doing a POST.
3) I am hitting the endpoint at 'https://api.twitter.com/1.1/direct_messages/events/new.json'
4) Oauth2, getting the access token(successfully)
5) Setting header as ('Content-Type', 'application/json').
6) Creating Authorization header as twitter mentions using consumer key, Nonce, Signature, Signature method, Timestamp, Version. Building the same as in "Guide" section of developer.twitter.com/en/docs/basics/authentication/guides/
7) On running the error code "{"errors":[{"code":32,"message":"Could not authenticate you."}]}".
Another important information that I had been using twitter old API to send DM that works perfect, only difference is it sends the request body in URL parameters instead of JSOn body but the authorization method remains same. As some new Functionality can only be achieved via Twitter New API and according to documentation the body needs to be sent via JSON format. Therefore the request part is changed but authorization is same.
Sample code:-
String accTok = 'redacted';
String conKey = 'redacted';
String conSec = 'redacted';
String accTokSec = 'redacted';
String theTweet = 'Hello world!';
String screenName ='some_test_username';
String jsonString = TwitterJsonReqGenerator.generateJSON(theTweet, screenName);
system.debug('JSON string ='+jsonString);
httpRequest newReq = new httpRequest();
newReq.setBody(jsonString);
newReq.setMethod('POST');
newReq.setEndpoint('https://api.twitter.com/1.1/direct_messages/events/new.json');
//Generate Nonce
string oAuth_nonce = EncodingUtil.base64Encode(blob.valueOf(string.valueOf(Crypto.getRandomInteger()+system.now().getTime())+string.valueOf(Crypto.getRandomInteger()))).replaceAll('[^a-z^A-Z^0-9]','');
map<String, String> heads = new map<String, String>{
'oauth_token'=>accTok,
'oauth_version'=>'1.0',
'oauth_nonce'=>oAuth_nonce,
'oauth_consumer_key'=>conKey,
'oauth_signature_method'=>'HMAC-SHA1',
'oauth_timestamp'=>string.valueOf(system.now().getTime()/1000)
};
//Alphabetize
string[] paramHeads = new string[]{};
paramHeads.addAll(heads.keySet());
paramHeads.sort();
string params = '';
for(String encodedKey : paramHeads){
params+=encodedKey+'%3D'+heads.get(encodedKey)+'%26';
}
//params+='status'+percentEncode('='+percentEncode(theTweet));
params+=percentEncode(theTweet);
//Build the base string
string sigBaseString = newReq.getMethod().toUpperCase()+'&'+EncodingUtil.urlEncode(newReq.getEndpoint(),'UTF-8')+'&'+params;
system.debug('signatureBaseString == '+sigBaseString);
//calculate signature
string sigKey = EncodingUtil.urlEncode(conSec,'UTF-8')+'&'+EncodingUtil.urlEncode(accTokSec,'UTF-8');
blob mac = crypto.generateMac('hmacSHA1', blob.valueOf(sigBaseString), blob.valueOf(sigKey));
string oauth_signature = EncodingUtil.base64Encode(mac);
heads.put(EncodingUtil.urlEncode('oauth_signature','UTF-8'), EncodingUtil.urlEncode(oauth_signature,'UTF-8'));
//build the authorization header
paramHeads.clear();
paramHeads.addAll(heads.keySet());
paramHeads.sort();
string oAuth_Body = 'OAuth ';
for(String key : paramHeads){
oAuth_Body += key+'="'+heads.get(key)+'", ';
}
oAuth_Body = oAuth_Body.subString(0, (oAuth_Body.length() - 2));
newReq.setHeader('Authorization', oAuth_Body);
system.debug('Authroization Header == '+oAuth_Body);
newReq.setHeader('Content-Type', 'application/json');
httpResponse httpRes = new http().send(newReq);
String response = httpRes.getBody();
system.debug(response);
Thanks
Prateek
I've written Twitter libraries and applications in the past, and the bst advice that I can give you is to use an existing implementation of OAuth instead of attempting to write your own. Re-implementing OAuth in new code is re-inventing the wheel, and it's a wheel that hates you. There are a number of robust and mature OAuth libraries that are free and/or open source.
Just happened to stumble on your query. I am posting a code(C#) (though it is a bit late) which worked for me to send DM to Twitter using the new API. Hope this helps. Thanks to Danny Tuppeny's blog
namespace TweetApp.Droid
{
class TweetDM
{
const string TwitterApiBaseUrl = "https://api.twitter.com/1.1/";
readonly string consumerKey, consumerKeySecret, accessToken, accessTokenSecret;
readonly HMACSHA1 sigHasher;
readonly DateTime epochUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public TweetDM(string consumerKey, string consumerKeySecret, string accessToken, string accessTokenSecret)
{
this.consumerKey = consumerKey;
this.consumerKeySecret = consumerKeySecret;
this.accessToken = accessToken;
this.accessTokenSecret = accessTokenSecret;
sigHasher = new HMACSHA1(new ASCIIEncoding().GetBytes(string.Format("{0}&{1}", consumerKeySecret, accessTokenSecret)));
}
public Task<string> Tweet(string text, string recipientID)
{
JSONObject jasonobject = new JSONObject
{
#event = new TwitterEvent
{
type = "message_create",
message_create = new msg_create
{
target = new tgt
{
recipient_id = recipientID
},
message_data = new msg_data
{
text = text
}
},
}
};
var JsonString =JsonConvert.SerializeObject(jasonobject);
var data4Auth = new Dictionary<string, string> {
};
return PrepareAuth("direct_messages/events/new.json", data4Auth, JsonString);
}
Task<string> PrepareAuth(string url, Dictionary<string, string> data4Auth, string JsonString)
{
var fullUrl = TwitterApiBaseUrl + url;
var timestamp = (int)((DateTime.UtcNow - epochUtc).TotalSeconds);
data4Auth.Add("oauth_consumer_key", consumerKey);
data4Auth.Add("oauth_signature_method", "HMAC-SHA1");
data4Auth.Add("oauth_timestamp", timestamp.ToString());
data4Auth.Add("oauth_nonce", "a"); // Required, but Twitter doesn't appear to use it, so "a" will do.
data4Auth.Add("oauth_token", accessToken);
data4Auth.Add("oauth_version", "1.0");
// Generate the OAuth signature and add it to our payload.
data4Auth.Add("oauth_signature", GenerateSignature(fullUrl, data4Auth));
// Build the OAuth HTTP Header from the data.
string oAuthHeader = GenerateOAuthHeader(data4Auth);
// Setting Content details
var JsonData = new StringContent(JsonString, Encoding.UTF8, "application/json");
return SendRequest(fullUrl, oAuthHeader, JsonData);
}
string GenerateSignature(string url, Dictionary<string, string> data)
{
var sigString = string.Join(
"&",
data
.Union(data)
.Select(kvp => string.Format("{0}={1}", Uri.EscapeDataString(kvp.Key), Uri.EscapeDataString(kvp.Value)))
.OrderBy(s => s)
);
var fullSigData = string.Format(
"{0}&{1}&{2}",
"POST",
Uri.EscapeDataString(url),
Uri.EscapeDataString(sigString.ToString())
);
return Convert.ToBase64String(sigHasher.ComputeHash(new ASCIIEncoding().GetBytes(fullSigData.ToString())));
}
string GenerateOAuthHeader(Dictionary<string, string> data)
{
return "OAuth " + string.Join(
", ",
data
.Where(kvp => kvp.Key.StartsWith("oauth_"))
.Select(kvp => string.Format("{0}=\"{1}\"", Uri.EscapeDataString(kvp.Key), Uri.EscapeDataString(kvp.Value)))
.OrderBy(s => s)
);
}
async Task<string> SendRequest(string fullUrl, string oAuthHeader, StringContent jsondata)
{
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Add("Authorization", oAuthHeader);
var httpResp = await http.PostAsync(fullUrl, jsondata);
var respBody = await httpResp.Content.ReadAsStringAsync();
return respBody;
}
}
}
// Classes for creating JSON body
public class JSONObject
{
public TwitterEvent #event;
}
public class TwitterEvent
{
public string type;
public msg_create message_create;
}
public class msg_create
{
public tgt target;
public msg_data message_data;
}
public class tgt
{
public string recipient_id;
}
public class msg_data
{
public string text;
}
}
To call:
var twitter = new TweetDM(consumerKey, consumerKeySecret, accessToken, accessTokenSecret);
await twitter.Tweet(textBox1.Text, textBox2.Text);
to do that i made a web service to send push (by referencing PushSharp library). I request web service through my web application. i retrieve list of device token from database(using web application) send to web service using for loop to send push. and get result/exception for each one. This process is very slow and take long long time to send notification. If anybody suggest me to what should i do i will be grateful to you.
public ActionResult SendNowToken(int certificateInfoId, string message, string certificate, int badgeNo, int pushtype, string password, string countryJsonString)
{
if (IsPushParameterValid(certificateInfoId, message, certificate, badgeNo, pushtype, password, countryJsonString))
{
var countryObject = new JavaScriptSerializer().Deserialize<Country>(countryJsonString);
var errorList = new List<ErrorList>();
byte[] certificatePath = System.IO.File.ReadAllBytes(HttpContext.Server.MapPath("~/Content/certificate/" + certificate));
foreach (var aDeviceToken in countryObject.DeviceTokens)
{
try
{
var serviceClient = new PushServiceSoapClient();
string serviceResult = serviceClient.SendPushNotification(message, badgeNo, pushtype, aDeviceToken.Token, certificatePath, password);
if (serviceResult != "Sent Notification")
{
var delimiters = new[] { ' ' };
string[] errorResult = serviceResult.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
string errorMessage = ConvertErrorCodeToErrorMessage(errorResult[0]);
var error = new ErrorList
{
CountryName = countryObject.CountryName,
ErrorTime = DateTime.Now,
ErrorMessage = errorMessage,
Token = aDeviceToken.Token
};
errorList.Add(error);
}
}
catch (Exception ex)
{
var error = new ErrorList
{
CountryName = countryObject.CountryName,
ErrorTime = DateTime.Now,
ErrorMessage = ex.Message,
Token = aDeviceToken.Token
};
errorList.Add(error);
}
}
if (errorList.Count != 0)
{
ViewBag.Message = "Push Notification does not send to country... ";
return PartialView("_SendAllError", errorList.ToList());
}
errorList.Clear();
}
return View();
}
My MVC4 application allows login using LinkedIn account. I want to pull all details that are avaible from linkedIn of the logged in User. Currently i have done the following.
In My AuthConfig.cs,
Dictionary<string, object> linkedInExtraData = new Dictionary<string, object>();
linkedInExtraData.Add("Icon", "../Images/linkedIn.png");
OAuthWebSecurity.RegisterClient(
client: new App_Start.LinkedInCustomClient("xxxxxxxxxxxx", "yyyyyyyyyyyyyyy"),
displayName: "LinkedIn",
extraData: linkedInExtraData);
In linkedInCustomClient.cs , from LinkedIn Developer Kit
public class LinkedInCustomClient : OAuthClient
{
private static XDocument LoadXDocumentFromStream(Stream stream)
{
var settings = new XmlReaderSettings
{
MaxCharactersInDocument = 65536L
};
return XDocument.Load(XmlReader.Create(stream, settings));
}
/// Describes the OAuth service provider endpoints for LinkedIn.
private static readonly ServiceProviderDescription LinkedInServiceDescription =
new ServiceProviderDescription
{
AccessTokenEndpoint =
new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/accessToken",
HttpDeliveryMethods.PostRequest),
RequestTokenEndpoint =
new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/requestToken?scope=r_fullprofile",
HttpDeliveryMethods.PostRequest),
UserAuthorizationEndpoint =
new MessageReceivingEndpoint("https://www.linkedin.com/uas/oauth/authorize",
HttpDeliveryMethods.PostRequest),
TamperProtectionElements =
new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
ProtocolVersion = ProtocolVersion.V10a
};
public LinkedInCustomClient(string consumerKey, string consumerSecret) :
base("linkedIn", LinkedInServiceDescription, consumerKey, consumerSecret) { }
/// Check if authentication succeeded after user is redirected back from the service provider.
/// The response token returned from service provider authentication result.
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "We don't care if the request fails.")]
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
// See here for Field Selectors API http://developer.linkedin.com/docs/DOC-1014
const string profileRequestUrl =
"https://api.linkedin.com/v1/people/~:(id,first-name,last-name,interests,headline,industry,summary,email-address,location:(name),picture-url,positions,associations,languages,honors,educations,date-of-birth,primary-twitter-account,three-current-positions,three-past-positions,group-memberships,specialties,skills)";
string accessToken = response.AccessToken;
string tokenSecret = (response as ITokenSecretContainingMessage).TokenSecret;
string Verifier = response.ExtraData.Values.First();
var profileEndpoint =
new MessageReceivingEndpoint(profileRequestUrl, HttpDeliveryMethods.GetRequest);
HttpWebRequest request =
WebWorker.PrepareAuthorizedRequest(profileEndpoint, accessToken);
try
{
using (WebResponse profileResponse = request.GetResponse())
{
using (Stream responseStream = profileResponse.GetResponseStream())
{
XDocument document = LoadXDocumentFromStream(responseStream);
return new AuthenticationResult(
isSuccessful: true,
provider: ProviderName,
providerUserId: userId,
userName: userName,
extraData: extraData);
}
}
}
catch (Exception exception)
{
return new AuthenticationResult(exception);
}
}
}
In my controller,
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (!result.IsSuccessful)
{
return RedirectToAction("ExternalLoginFailure");
}
I need to get the following details in my controller as authentication result.
(id,first-name,last-name,interests,headline,industry,summary,email-address,location:(name),picture-url,positions,associations,languages,honors,educations,date-of-birth,primary-twitter-account,three-current-positions,three-past-positions,group-memberships,specialties,skills)
The response of your request from LinkedIn will be a xml file. The format and fields are mentioned in LinkedIn Profile Fields
For getting email field, you need to modify your request token url as
RequestTokenEndpoint = new MessageReceivingEndpoint("https://api.linkedin.com/uas/oauth/requestToken?scope=r_fullprofile+r_emailaddress",
HttpDeliveryMethods.PostRequest),
You can get the fields as required in the following code
XDocument document = LoadXDocumentFromStream(responseStream);
Eg : For getting the first name field from the xml file,
var firstName = document.Root.Element("first-name").Value;
Fields like languages, positions, skills etc will be returned as structured objects as part of the profile.
Eg : Language field.
var Lang = document.Root.Element("languages");
var languages = new List<string>();
if (Lang != null)
{
foreach (var l in Lang.Elements())
{
if (l.Element("language") != null && l.Element("language").Element("name") != null)
{
languages.Add(l.Element("language").Element("name").Value);
}
}
}
Then you can add fields to "extraData" which can be accessed in the controller.
extraData.Add("firstName", firstName);
extraData.Add("languages", lang);