ASP.Net MVC5, Google OAuth 2.0 and Youtube API - c#

I need some help regarding mvc 5 using the google login provider and getting some youtube data. right now i think i get things a little mixed up. i'm not new to mvc but to version 5's owin middleware features. well, and not experienced in implementing oauth 2.0.
What i want:
Login to my MVC5 Application via Google.
Read some Youtube information from the logged in user.
What i have done so far:
Followed this Google OAuth 2.0 tutorial: Web applications (ASP.NET MVC).
Installed Google.Apis.Auth.MVC via NuGet.
Implemented AppFlowMetadata and AuthCallbackController as described.
Configured the redirect uri to "/AuthCallback/IndexAsync" as described.
Implemented a YoutubeController with the following action just to dump out some data:
public async Task<ActionResult> IndexAsync()
{
var result =
await new AuthorizationCodeMvcApp(this, new AppFlowMetadata())
.AuthorizeAsync(cancellationToken);
if (result.Credential == null)
{
return new RedirectResult(result.RedirectUri);
}
else
{
var service = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "MyYoutubeApplication"
});
var playlists = service.Playlists.List("contentDetails, snippet");
playlists.Mine = true;
var list = await playlists.ExecuteAsync();
var json = new JavaScriptSerializer().Serialize(list);
ViewBag.Message = json;
return View();
}
}
So what this does, when trying to access /Youtube/IndexAsync is redirecting me to google, asking for my credentials.
when entered, i'm asked if i'm ok with the permission asked by the application. after confirming, i get redirected to my page, showing my /Youtube/IndexAsync page with the requested data. so far so good, but that's not quite what i want.
what (i think) i have done here is that i completely bypassed the asp.net identity system. the user is not logged in to my application let alone registered.
i want the user to log in with google, register in my application and provide access to his youtube data. then, when on a specific page, retrieve data from the user's youtube account.
What i also have tried:
Following this ASP.Net MVC5 Tutorial
This tutorial does not mention the NuGet package "Google.Apis.Auth.MVC" and talks something about a magic "/signin-google" redirect uri".
This also works, but breaks the solution above, complaining about a wrong redirect uri.
When using this approach, it seems not right to me call AuthorizeAsync in YoutubeController again, since i should already be authorized.
So i'm looking for some light in the dark, telling me what i'm mixing all together :) I hope the question is not as confused as i am right now.

I managed to do this using GooglePlus, haven't tried Google. Here's what I did:
Install the nugets:
> Install-Package Owin.Security.Providers
> Install-Package Google.Apis.Youtube.v3
Add this to Startup.auth.cs:
var g = new GooglePlusAuthenticationOptions();
g.ClientId = Constants.GoogleClientId;
g.ClientSecret = Constants.GoogleClientSecret;
g.RequestOfflineAccess = true; // for refresh token
g.Provider = new GooglePlusAuthenticationProvider
{
OnAuthenticated = context =>
{
context.Identity.AddClaim(new Claim(Constants.GoogleAccessToken, context.AccessToken));
if (!String.IsNullOrEmpty(context.RefreshToken))
{
context.Identity.AddClaim(new Claim(Constants.GoogleRefreshToken, context.RefreshToken));
}
return Task.FromResult<object>(null);
}
};
g.Scope.Add(Google.Apis.YouTube.v3.YouTubeService.Scope.YoutubeReadonly);
g.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
app.UseGooglePlusAuthentication(g);
The above code does two things:
Enable authentication via. Google+
Requests for the access token and the refresh token. The tokens are then added as a claim in the GooglePlus middleware.
Create a method that will store the claims containing the token to the database. I have this in the AccountController.cs file
private async Task StoreGooglePlusAuthToken(ApplicationUser user)
{
var claimsIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
if (claimsIdentity != null)
{
// Retrieve the existing claims for the user and add the google plus access token
var currentClaims = await UserManager.GetClaimsAsync(user.Id);
var ci = claimsIdentity.FindAll(Constants.GoogleAccessToken);
if (ci != null && ci.Count() != 0)
{
var accessToken = ci.First();
if (currentClaims.Count() <= 0)
{
await UserManager.AddClaimAsync(user.Id, accessToken);
}
}
ci = claimsIdentity.FindAll(Constants.GoogleRefreshToken);
if (ci != null && ci.Count() != 0)
{
var refreshToken = ci.First();
if (currentClaims.Count() <= 1)
{
await UserManager.AddClaimAsync(user.Id, refreshToken);
}
}
}
You'll need to call it in 2 places in the AccountController.cs: Once in ExternalLoginCallback:
case SignInStatus.Success:
var currentUser = await UserManager.FindAsync(loginInfo.Login);
if (currentUser != null)
{
await StoreGooglePlusAuthToken(currentUser);
}
return RedirectToLocal(returnUrl);
and once in ExternalLoginConfirmation:
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded)
{
await StoreGooglePlusAuthToken(user);
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
return RedirectToLocal(returnUrl);
}
}
Now that we've got the users access token and refresh token we can use this to authenticate the user.
I tried a simple search I saw in the examples and it worked:
private async Task<Models.YouTubeViewModel> Search(string searchTerm)
{
var user = (ClaimsPrincipal)Thread.CurrentPrincipal;
var at = user.Claims.FirstOrDefault(x => x.Type == Constants.GoogleAccessToken);
var rt = user.Claims.FirstOrDefault(x => x.Type == Constants.GoogleRefreshToken);
if (at == null || rt == null)
throw new HttpUnhandledException("Access / Refresh Token missing");
TokenResponse token = new TokenResponse
{
AccessToken = at.Value,
RefreshToken = rt.Value
};
var cred = new UserCredential(new GoogleAuthorizationCodeFlow(
new GoogleAuthorizationCodeFlow.Initializer()
{
ClientSecrets = new ClientSecrets()
{
ClientId = Constants.GoogleClientId,
ClientSecret = Constants.GoogleClientSecret
}
}
),
User.Identity.GetApplicationUser().UserName,
token
);
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
ApplicationName = this.GetType().ToString(),
HttpClientInitializer = cred,
});
var searchListRequest = youtubeService.Search.List("snippet");
searchListRequest.Q = searchTerm;
searchListRequest.MaxResults = 50;
// Call the search.list method to retrieve results matching the specified query term.
var searchListResponse = await searchListRequest.ExecuteAsync();
Models.YouTubeViewModel vm = new Models.YouTubeViewModel(searchTerm);
foreach (var searchResult in searchListResponse.Items)
{
switch (searchResult.Id.Kind)
{
case "youtube#video":
vm.Videos.Add(new Models.Result(searchResult.Snippet.Title, searchResult.Id.VideoId));
break;
case "youtube#channel":
vm.Channels.Add(new Models.Result(searchResult.Snippet.Title, searchResult.Id.ChannelId));
break;
case "youtube#playlist":
vm.Playlists.Add(new Models.Result(searchResult.Snippet.Title, searchResult.Id.PlaylistId));
break;
}
}
return vm;
}
Model Classes
public class Result
{
public string Title { get; set; }
public string Id { get; set; }
public Result() { }
public Result(string title, string id)
{
this.Title = title;
this.Id = id;
}
}
public class YouTubeViewModel
{
public string SearchTerm { get; set; }
public List<Result> Videos { get; set; }
public List<Result> Playlists { get; set; }
public List<Result> Channels { get; set; }
public YouTubeViewModel()
{
Videos = new List<Result>();
Playlists = new List<Result>();
Channels = new List<Result>();
}
public YouTubeViewModel(string searchTerm)
:this()
{
SearchTerm = searchTerm;
}
}
Reference: http://blogs.msdn.com/b/webdev/archive/2013/10/16/get-more-information-from-social-providers-used-in-the-vs-2013-project-templates.aspx

Related

Confirm Email in asp.net core web api

This API is intended for a mobile application. The goal is to let the user confirm the email upon registration. When the user registers, a confirmation link is generated and sent over the email. I've done it the same way in a MVC project, it worked fine, but in a Web API project looks like it ain't gonna cut.
Now when the user clicks that link, the respective action method should be hit and do the job.
The only problem is, the ConfirmEmail action method is just not getting triggered when clicking the confirmation link although it looked fine.
Here are the main configurations which might help
MVC service configuration
services.AddMvc(options =>
{
options.EnableEndpointRouting = true;
options.Filters.Add<ValidationFilter>();
})
.AddFluentValidation(mvcConfiguration => mvcConfiguration.RegisterValidatorsFromAssemblyContaining<Startup>())
.SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_3_0);
Identity Service
public async Task<AuthenticationResult> RegisterAsync(string email, string password)
{
var existingUser = await _userManager.FindByEmailAsync(email);
if(existingUser != null)
{
return new AuthenticationResult { Errors = new[] { "User with this email address exists" } };
}
// generate user
var newUser = new AppUser
{
Email = email,
UserName = email
};
// register user in system
var result = await _userManager.CreateAsync(newUser, password);
if (!result.Succeeded)
{
return new AuthenticationResult
{
Errors = result.Errors.Select(x => x.Description)
};
}
// when registering user, assign him user role, also need to be added in the JWT!!!
await _userManager.AddToRoleAsync(newUser, "User");
// force user to confirm email, generate token
var token = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
// generate url
var confirmationLink = _urlHelper.Action("ConfirmEmail", "IdentityController",
new { userId = newUser.Id, token = token }, _httpRequest.HttpContext.Request.Scheme);
// send it per email
var mailresult =
await _emailService.SendEmail(newUser.Email, "BingoApp Email Confirmation",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(confirmationLink)}'>clicking here</a>.");
if (mailresult)
return new AuthenticationResult { Success = true };
else
return new AuthenticationResult { Success = false, Errors = new List<string> { "Invalid Email Address"} };
}
Controller
[HttpPost(ApiRoutes.Identity.Register)]
public async Task<IActionResult> Register([FromBody] UserRegistrationRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest(new AuthFailedResponse
{
Errors = ModelState.Values.SelectMany(x => x.Errors.Select(xx => xx.ErrorMessage))
});
}
// register the incoming user data with identity service
var authResponse = await _identityService.RegisterAsync(request.Email, request.Password);
if (!authResponse.Success)
{
return BadRequest(new AuthFailedResponse
{
Errors = authResponse.Errors
});
}
// confirm registration
return Ok();
}
[HttpGet]
public async Task<IActionResult> ConfirmEmail(string userId, string token)
{
if (userId == null || token == null)
{
return null;
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return null;
}
var result = await _userManager.ConfirmEmailAsync(user, token);
if (result.Succeeded)
{
await _emailService.SendEmail(user.Email, "BingoApp - Successfully Registered", "Congratulations,\n You have successfully activated your account!\n " +
"Welcome to the dark side.");
}
return null;
}
Your _urlHelper.Action(..) looks a bit suspicious to me.
I'm not sure you should pass the full controller name, that is, including the actual word controller.
Try _urlHelper.Action("ConfirmEmail", "Identity", instead.
As a tip: I try to avoid magic strings like these by using nameof(IdentityController) because it will return the controller name without the controller postfix.

Web API facebook, email claim returns null

Code is as follows
startup auth:
app.UseFacebookAuthentication(new FacebookAuthenticationOptions
{
AppId = "xxx",
AppSecret = "xxx",
BackchannelHttpHandler = new FacebookBackChannelHandler(),
UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email,first_name,last_name,location",
Scope = { "email" }
});
in the accountcontroler RegisterExternal class i call the following:
var info = await AuthenticationManager_GetExternalLoginInfoAsync_WithExternalBearer();
Which is this class:
private async Task<ExternalLoginInfo> AuthenticationManager_GetExternalLoginInfoAsync_WithExternalBearer()
{
ExternalLoginInfo loginInfo = null;
var result = await Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
if (result != null && result.Identity != null)
{
var idClaim = result.Identity.FindFirst(ClaimTypes.NameIdentifier);
if (idClaim != null)
{
loginInfo = new ExternalLoginInfo()
{
DefaultUserName = result.Identity.Name == null ? "" : result.Identity.Name.Replace(" ", ""),
Login = new UserLoginInfo(idClaim.Issuer, idClaim.Value),
};
}
}
return loginInfo;
}
This is because the RegisterExternal class on default will use authentication type cookie. Whenever i use it it will return null, so after surfing the web I've noticed it is necessary add this code which in turn will use the bearer for authentication, this will result in a reply where the username and the identity are no longer null, thus authorized. (see picture below)
Return object (username and login)
But, when i want to claim the email, i cannot do this. It will always return null no matter what i do.
I was running into the same issue and solved it by using the Facebook nuget package to get extra fields.
In my application I have implemented FacebookAuthenticationProvider and overridden the Authenticated(...) method with the following:
public class FacebookAuthProvider : FacebookAuthenticationProvider
{
public override Task Authenticated(FacebookAuthenticatedContext context)
{
var accessTokenClaim = new Claim("ExternalAccessToken", context.AccessToken, "urn:facebook:access_token");
context.Identity.AddClaim(accessTokenClaim);
var extraClaims = GetAdditionalFacebookClaims(accessTokenClaim);
context.Identity.AddClaim(new Claim(ClaimTypes.Email, extraClaims.First(k => k.Key == "email").Value.ToString()));
context.Identity.AddClaim(new Claim("Provider", context.Identity.AuthenticationType));
context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirstValue(ClaimTypes.Name)));
var userDetail = context.User;
var link = userDetail.Value<string>("link") ?? string.Empty;
context.Identity.AddClaim(new Claim("link", link));
context.Identity.AddClaim(new Claim("FacebookId", userDetail.Value<string>("id")));
return System.Threading.Tasks.Task.FromResult(0);
}
private static JsonObject GetAdditionalFacebookClaims(Claim accessToken)
{
var fb = new FacebookClient(accessToken.Value);
return fb.Get("me", new { fields = new[] { "email", "first_name", "last_name" } }) as JsonObject;
}
}
My Startup.cs has this within Configuration(IAppBuilder app):
FacebookAuthOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
{
AppId = "YOUR APP ID",
AppSecret = "YOUR APP SECRET",
Microsoft.Owin.PathString("/Account/ExternalCallBack"), // whatever your external callback url is
Provider = new FacebookAuthProvider()
};
FacebookAuthOptions.Scope.Add("email");
app.UseFacebookAuthentication(FacebookAuthOptions);
I've no idea why the email was not being populated in the first place, but this approach worked for me.

Authenticate to Yammer in c# console application

I am using the NuGet Yammer API and I am trying to simply authenticate and display the token as a test.
Unfortunately I can't seem to get it working. I am new to this but there is no documentation on the NuGet Yammer API and it will be a console application. All the examples and documentation on the Yammer developers page show doing this from a web based appication.
My code so far:
static void Main(string[] args)
{
var myConfig = new ClientConfigurationContainer
{
ClientCode = null,
ClientId = "CODEHERE",
ClientSecret = "CODEHERE"
};
var myYammer = new YammerClient(myConfig);
var test = myYammer.GetToken();
Console.WriteLine("Token" + test);
Console.ReadLine();
}
It's an OAuth authentication, you must interact with Yammer OAuth webpage to obtain a token.
You should look in the asp.net mvc example in sources on Github.
In the HomeController.cs :
[HttpPost]
public ActionResult Index(IndexViewModel model)
{
if (ModelState.IsValid)
{
var myConfig = new ClientConfigurationContainer
{
ClientCode = null,
ClientId = model.ClientId,
ClientSecret = model.ClientSecret,
RedirectUri = Request.Url.AbsoluteUri + Url.Action("AuthCode")
};
var myYammer = new YammerClient(myConfig);
// Obtain the URL of Yammer Authorisation Page
var url = myYammer.GetLoginLinkUri();
this.TempData["YammerConfig"] = myConfig;
// Jump to the url page
return Redirect(url);
}
return View(model);
}
And Yammer redirect you here:
public ActionResult AuthCode(String code)
{
if (!String.IsNullOrWhiteSpace(code))
{
var myConfig = this.TempData["YammerConfig"] as ClientConfigurationContainer;
myConfig.ClientCode = code;
var myYammer = new YammerClient(myConfig);
// var yammerToken = myYammer.GetToken();
// var l = myYammer.GetUsers();
// var t= myYammer.GetImpersonateTokens();
// var i = myYammer.SendInvitation("test#test.fr");
// var m = myYammer.PostMessage("A test from here", 0, "Event");
return View(myYammer.GetUserInfo());
}
return null;
}
The person who wrote the API also wrote an article on how to use it, which is here:
http://fullsaas.blogspot.fr/2013/05/a-simple-net-wrapper-of-yammer-api.html
This may also be useful:
https://blogs.technet.com/b/speschka/archive/2013/10/05/using-the-yammer-api-in-a-net-client-application.aspx

C# Google drive sdk. How to get a list of google drive folders?

I'm writing a program to allow a user to upload files to their Google Drive account. I have the upload part working and am using OAuth2. The issue I'm currently having is getting a list of folders from the users Drive account.
I found some code that is supposed to do this using the .setUserCredentials method, but it doesn't work:
DocumentsService service1 = new DocumentsService("project");
service1.setUserCredentials("user","pass");
FolderQuery query1 = new FolderQuery();
// Make a request to the API and get all documents.
DocumentsFeed feed = service1.Query(query1);
// Iterate through all of the documents returned
foreach (DocumentEntry entry in feed.Entries)
{
var blech = entry.Title.Text;
}
Nothing is returned. Ideally, I want to use OAuth2 to do this. I've been trying with the following code, trying to set the authentication token, but I always get denied access:
String CLIENT_ID = "clientid";
String CLIENT_SECRET = "secretid";
var docprovider = new NativeApplicationClient(GoogleAuthenticationServer.Description, CLIENT_ID, CLIENT_SECRET);
var docstate = GetDocAuthentication(docprovider);
DocumentsService service1 = new DocumentsService("project");
service1.SetAuthenticationToken(docstate.RefreshToken);
FolderQuery query1 = new FolderQuery();
DocumentsFeed feed = service1.Query(query1); //get error here
// Iterate through all of the documents returned
foreach (DocumentEntry entry in feed.Entries)
{
// Print the title of this document to the screen
var blech = entry.Title.Text;
}
..
private static IAuthorizationState GetDocAuthentication(NativeApplicationClient client)
{
const string STORAGE = "storagestring";
const string KEY = "keystring";
string scope = "https://docs.google.com/feeds/default/private/full/-/folder";
// Check if there is a cached refresh token available.
IAuthorizationState state = AuthorizationMgr.GetCachedRefreshToken(STORAGE, KEY);
if (state != null)
{
try
{
client.RefreshToken(state);
return state; // Yes - we are done.
}
catch (DotNetOpenAuth.Messaging.ProtocolException ex)
{
}
}
// Retrieve the authorization from the user.
state = AuthorizationMgr.RequestNativeAuthorization(client, scope);
AuthorizationMgr.SetCachedRefreshToken(STORAGE, KEY, state);
return state;
}
Specifically, I get "Execution of request failed: https://docs.google.com/feeds/default/private/full/-/folder - The remote server returned an error: (401) Unauthorized".
I've also tried:
var docauth = new OAuth2Authenticator<NativeApplicationClient>(docprovider, GetDocAuthentication);
DocumentsService service1 = new DocumentsService("project");
service1.SetAuthenticationToken(docauth.State.AccessToken);
but "State" is always null, so I get a null object error. What am I doing wrong and how is this done?
You should use the Drive SDK, not the Documents List API, which allows you to list folders. You can use "root" as a folderId if you want to list the root directory.
I actually implemented the v3 version of the GDrive SDK for .NET and needed to search for folders as well.
I prefer requesting uniquely all folders instead of getting all files and then performing a LinQ query to keep just the folders.
This is my implementation:
private async Task<bool> FolderExistsAsync(string folderName)
{
var response = await GetAllFoldersAsync();
return response.Files
.Where(x => x.Name.ToLower() == folderName.ToLower())
.Any();
}
private async Task<Google.Apis.Drive.v3.Data.FileList> GetAllFoldersAsync()
{
var request = _service.Files.List();
request.Q = "mimeType = 'application/vnd.google-apps.folder'";
var response = await request.ExecuteAsync();
return response;
}
You could request the name on the Q this way as well:
request.Q = $"mimeType = 'application/vnd.google-apps.folder' and name = '{folderName}'";
Which would lead and simplify things to (obviating null checking):
private async Task<bool> FolderExistsAsync(string folderName)
{
var response = await GetDesiredFolder(folderName);
return response.Files.Any();
}
private async Task<FileList> GetDesiredFolder(string folderName)
{
var request = _service.Files.List();
request.Q = $"mimeType = 'application/vnd.google-apps.folder' and name = '{folderName}'";
var response = await request.ExecuteAsync();
return response;
}
private IEnumerable<DocumentEntry> GetFolders(string id) {
if (IsLogged) {
var query = new FolderQuery(id)
{
ShowFolders = true
};
var feed = GoogleDocumentsService.Query(query);
return feed.Entries.Cast<DocumentEntry>().Where(x => x.IsFolder).OrderBy(x => x.Title.Text);
}
return null;
}
...
var rootFolders = GetFolders("root");
if (rootFolders != null){
foreach(var folder in rootFolders){
var subFolders = GetFolders(folder.ResourceId);
...
}
}
where GoogleDocumentsService is a instance of DocumentsService and IsLogged is a success logged flag.
I got this way to get list of folders from google drive
FilesResource.ListRequest filelist= service.Files.List();
filelist.Execute().Items.ToList().Where(x => x.MimeType == "application/vnd.google-apps.folder").ToList()

Accessing Google Docs with GData

Working Platform: ASP.NET 4.0 C# ( Framework Agnostic )
Google GData is my dependency
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Documents;
I have two pages Auth and List.
Auth redirects to Google Server like this
public ActionResult Auth()
{
var target = Request.Url.ToString().ToLowerInvariant().Replace("auth", "list");
var scope = "https://docs.google.com/feeds/";
bool secure = false, session = true;
var authSubUrl = AuthSubUtil.getRequestUrl(target, scope, secure, session);
return new RedirectResult(authSubUrl);
}
Now it reaches the List Page if Authentication is successful.
public ActionResult List()
{
if (Request.QueryString["token"] != null)
{
String singleUseToken = Request.QueryString["token"];
string consumerKey = "www.blahblah.net";
string consumerSecret = "my_key";
string sessionToken = AuthSubUtil.exchangeForSessionToken(singleUseToken, null).ToString();
var authFactory = new GOAuthRequestFactory("writely", "qwd-asd-01");
authFactory.Token = sessionToken;
authFactory.ConsumerKey = consumerKey;
authFactory.ConsumerSecret = consumerSecret;
//authFactory.TokenSecret = "";
try
{
var service = new DocumentsService(authFactory.ApplicationName) { RequestFactory = authFactory };
var query = new DocumentsListQuery();
query.Title = "project";
var feed = service.Query(query);
var result = feed.Entries.ToList().ConvertAll(a => a.Title.Text);
return View(result);
}
catch (GDataRequestException gdre)
{
throw;
}
}
}
This fails at the line var feed = service.Query(query); with the error
Execution of request failed: https://docs.google.com/feeds/default/private/full?title=project
The HttpStatusCode recieved on the catch block is HttpStatusCode.Unauthorized
What is wrong with this code? Do I need to get TokenSecret? If so how?
You need to request a token from Google and use it to intialize your DocumentsService instance.
Here's an example using Google's ContactsService. It should be the same for the DocumentsService.
Service service = new ContactsService("My Contacts Application");
service.setUserCredentials("your_email_address_here#gmail.com", "yourpassword");
var token = service.QueryClientLoginToken();
service.SetAuthenticationToken(token);
But as you mentioned, you are using AuthSub. I jumped the gun a bit too fast.
I see that you are requesting a session token. According to the documentation of the API you must use the session token to authenticate requests to the service by placing the token in the Authorization header. After you've set the session token, you can use the Google Data APIs client library.
Here's a complete example (by Google) on how to use AuthSub with the .NET client library:
http://code.google.com/intl/nl-NL/apis/gdata/articles/authsub_dotnet.html
Let me include a shortened example:
GAuthSubRequestFactory authFactory =
new GAuthSubRequestFactory("cl", "TesterApp");
authFactory.Token = (String) Session["token"];
CalendarService service = new CalendarService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
EventQuery query = new EventQuery();
query.Uri = new Uri("http://www.google.com/calendar/feeds/default/private/full");
EventFeed calFeed = service.Query(query);
foreach (Google.GData.Calendar.EventEntry entry in calFeed.Entries)
{
//...
}
And if I see correctly your example code pretty follows the same steps, except that you set the ConsumerKey and ConsumerSecret for the AuthFactory which is not done in the example by Google.
Used the 3-legged OAuth in the Google Data Protocol Client Libraries
Sample Code
string CONSUMER_KEY = "www.bherila.net";
string CONSUMER_SECRET = "RpKF7ykWt8C6At74TR4_wyIb";
string APPLICATION_NAME = "bwh-wssearch-01";
string SCOPE = "https://docs.google.com/feeds/";
public ActionResult Auth()
{
string callbackURL = String.Format("{0}{1}", Request.Url.ToString(), "List");
OAuthParameters parameters = new OAuthParameters()
{
ConsumerKey = CONSUMER_KEY,
ConsumerSecret = CONSUMER_SECRET,
Scope = SCOPE,
Callback = callbackURL,
SignatureMethod = "HMAC-SHA1"
};
OAuthUtil.GetUnauthorizedRequestToken(parameters);
string authorizationUrl = OAuthUtil.CreateUserAuthorizationUrl(parameters);
Session["parameters"] = parameters;
ViewBag.AuthUrl = authorizationUrl;
return View();
}
public ActionResult List()
{
if (Session["parameters"] != null)
{
OAuthParameters parameters = Session["parameters"] as OAuthParameters;
OAuthUtil.UpdateOAuthParametersFromCallback(Request.Url.Query, parameters);
try
{
OAuthUtil.GetAccessToken(parameters);
GOAuthRequestFactory authFactory = new GOAuthRequestFactory("writely", APPLICATION_NAME, parameters);
var service = new DocumentsService(authFactory.ApplicationName);
service.RequestFactory = authFactory;
var query = new DocumentsListQuery();
//query.Title = "recipe";
var feed = service.Query(query);
var docs = new List<string>();
foreach (DocumentEntry entry in feed.Entries)
{
docs.Add(entry.Title.Text);
}
//var result = feed.Entries.ToList().ConvertAll(a => a.Title.Text);
return View(docs);
}
catch (GDataRequestException gdre)
{
HttpWebResponse response = (HttpWebResponse)gdre.Response;
//bad auth token, clear session and refresh the page
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
Session.Clear();
Response.Write(gdre.Message);
}
else
{
Response.Write("Error processing request: " + gdre.ToString());
}
throw;
}
}
else
{
return RedirectToAction("Index");
}
}
This 2-legged sample never worked for me for google docs.

Categories

Resources