I have a list of emails.
For each email, I need to create a Google Drive folder and share it with the given email.
How can I do it programmically?
I am using ASP.NET 4.0.
First of all you need to make sure you have application with clientid/secret and correct redirect uri configured. For my case - it's a desktop application:
So far you'll get clientId/secret key:
Now it's time to write some codes!
Step 1 - authorization:
private async static Task<UserCredential> Auth(ClientSecrets clientSecrets)
{
return await GoogleWebAuthorizationBroker.AuthorizeAsync(clientSecrets, Scopes, "user", CancellationToken.None);
}
Step 2 - construct your client for google drive:
private static DriveService GetService(UserCredential credential)
{
return new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MyApplicationName",
});
}
Step 3 - create folder (or any other content):
private static string CreateFolder(DriveService service, string folderName)
{
var file = new File {Title = folderName, MimeType = "application/vnd.google-apps.folder"};
var result = service.Files.Insert(file).Execute();
return result.Id;
}
Step 4 - share it!
/// <summary>
/// Share content. Doc link: https://developers.google.com/drive/v2/reference/permissions/insert
/// </summary>
private static void Share(DriveService service, string fileId, string value, string type, string role)
{
var permission = new Permission {Value = value, Type = type, Role = role};
service.Permissions.Insert(permission, fileId).Execute();
}
And finally usage of whole thing:
static void Main(string[] args)
{
var ClientId = "MySecredId";
var SecretKey = "MySecretKey";
var Scopes = new[] { DriveService.Scope.DriveFile, DriveService.Scope.Drive };
var secrets = new ClientSecrets { ClientId = ClientId, ClientSecret = SecretKey };
var credentials = Auth(secrets).Result;
var service = GetService(credentials);
var folderId = CreateFolder(service, "folderName");
Share(service, folderId, "user#gmail.com", "user", "reader");
}
For list of emails you can do pretty same thing creating/sharing content in a loop for every email you have.
Some usefull links:
Creating files
Sharing files
Also
You'll need Google.Apis.Drive.v2 nuget package
The steps to perform this hinge on authenticating with Google first. Once you've done that you'll be able to access the Drive API to perform the actions you'd like. The following links walk you through everything you need to do.
Step 1: Authenticate (server side in your case as you're using ASP.NET)
https://developers.google.com/drive/web/auth/web-server
Step 2: Create your folders
https://developers.google.com/drive/web/folder
Step 3: Share your folders
https://developers.google.com/drive/web/manage-sharing
Take a look in below link. It have full course on Google Drive !!
https://www.codeschool.com/courses/discover-drive
Related
I have my web api that uploads and reads an excel file from the client app and then afterwards saves the data into the database, the application works perfect on locally server but the problem comes when the application is deployed to azure server it returns error 500 internal server error therefore i don't understand why this happens and and don't know how i can track to understand what might be the cause below are my code blocks.
My Interface Class
public interface UploadExcelInterface
{
Task UploadMultipleClients(Client obj);
}
My Service Implementation
public class UploadExcelService : UploadExcelInterface
{
private readonly DbContext _connect;
private readonly IHttpContextAccessor httpContextAccessor;
public UploadExcelService(DbContext _connect, IHttpContextAccessor httpContextAccessor)
{
this._connect = _connect;
this.httpContextAccessor = httpContextAccessor;
}
public async Task UploadMultipleClients(Client obj)
{
var file = httpContextAccessor.HttpContext.Request.Form.Files[0];
if (file != null && file.Length > 0)
{
var folderName = Path.Combine("Datas", "ClientUPloads");
var pathToSave = Path.Combine(Directory.GetCurrentDirectory(), folderName);
var fileName = Guid.NewGuid() + ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
var fullPath = Path.Combine(pathToSave, fileName);
var clientsList = new List<Client>();
using (var fileStream = new FileStream(fullPath, FileMode.Create))
{
await file.CopyToAsync(fileStream);
FileInfo excelFile = new FileInfo(Path.Combine(pathToSave, fileName));
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
using (ExcelPackage package = new ExcelPackage(excelFile))
{
ExcelWorksheet worksheet = package.Workbook.Worksheets[0];
var rowcount = worksheet.Dimension.Rows;
for (int row = 2; row <= rowcount; row++)
{
var Names = (worksheet.Cells[row,2].Value ?? string.Empty).ToString().Trim();
var Address = (worksheet.Cells[row,3].Value ?? string.Empty).ToString().Trim();
var Title = (worksheet.Cells[row,4].Value ?? string.Empty).ToString().Trim();
var Product = (worksheet.Cells[row,5].Value ?? string.Empty).ToString().Trim();
var Order = (worksheet.Cells[row,6].Value ?? string.Empty).ToString().Trim();
var Email = (worksheet.Cells[row,7].Value ?? string.Empty).ToString().Trim();
var Price = (worksheet.Cells[row,8].Value ?? string.Empty).ToString().Trim();
clientsList.Add(new Client
{
Names = Names,
Address = Address,
Title = Title,
Product = Product,
Order = Order,
Email = Email,
Price = Price,
}
}
//adding clients into the database
foreach (Client client in clientsList)
{
var exist = _connect.client.Any(x => x.Email == client.Email);
if (!exist)
{
await _connect.client.AddAsync(client);
}
}
await _connect.SaveChangesAsync();
}
}
}
My Controller Class
[HttpPost]
public async Task UploadMultipleClients([FromForm] Client obj)
{
await uploadExcelInterface.UploadMultipleClients(obj);
}
}
Please any help regarding this error that am getting from the server, and addition on that is it possible to get the data from the excel file without uploading it to server if yes how? because i tried adding the file to memory stream an reading it from memory but it appers not work, any suggestions thanks.
My answer may not help you solve the problem directly, but it can locate the error step by step. After we fix the error, we should be able to solve the problem in this thread.
Suggestions
Please make sure you have inclue EPPlus library in your deploy content.
Enabling ASP.NET Core stdout log (Windows Server)
Azure App Service - Configure Detailed Error Logging
Why
After tested, I am sure azure webapp can support EPPlus. For 500 error, as we don't have a more specific error message to refer to, we can't quickly locate the problem. Following the suggested method, you will surely see some useful information.
E.g:
The class library of EPPlus was not found.
Folders such as Datas are not created.
The database connection string, the test environment and the production environment may be different.
...
I am following a sample from https://github.com/davidebbo/AzureWebsitesSamples
private static ResourceManagementClient _resourceGroupClient;
private static WebSiteManagementClient _websiteClient;
private static AzureEnvironment _environment;
private static DnsManagementClient _dnsClient;
static string _ClientId = Startup.StaticConfig.GetValue<string>("Azure:ClientId");
static string _ClientKey = Startup.StaticConfig.GetValue<string>("Azure:ClientSecret");
static string _TenantId = Startup.StaticConfig.GetValue<string>("Azure:TenantId");
static string _SubscriptionId = Startup.StaticConfig.GetValue<string>("Azure:SubscriptionId");
static string _ResourceGroupName = Startup.StaticConfig.GetValue<string>("Azure:ResourceGroupName");
static string _AppName = Startup.StaticConfig.GetValue<string>("Azure:AppName");
public static string ResourceGroupName { get => _ResourceGroupName; set => _ResourceGroupName = value; }
public static async Task MainAsync()
{
// Set Environment - Choose between Azure public cloud, china cloud and US govt. cloud
_environment = AzureEnvironment.PublicEnvironments[EnvironmentName.AzureCloud];
// Get the credentials
TokenCloudCredentials cloudCreds = await GetCredsFromServicePrincipal();
var tokenCreds = new TokenCredentials(cloudCreds.Token);
//var loggingHandler = new LoggingHandler(new HttpClientHandler());
// Create our own HttpClient so we can do logging
var httpClient = new HttpClient();
// Use the creds to create the clients we need
_resourceGroupClient = new ResourceManagementClient(_environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager), tokenCreds );
_resourceGroupClient.SubscriptionId = cloudCreds.SubscriptionId;
_websiteClient = new WebSiteManagementClient(_environment.GetEndpointAsUri(AzureEnvironment.Endpoint.ResourceManager), tokenCreds);
_websiteClient.SubscriptionId = cloudCreds.SubscriptionId;
_dnsClient = new DnsManagementClient(tokenCreds);
AddCustomDomainToSite("mycustomdomain.com");
}
private static async Task<TokenCloudCredentials> GetCredsFromServicePrincipal()
{
// Quick check to make sure we're not running with the default app.config
if (_SubscriptionId[0] == '[')
{
throw new Exception("You need to enter your appSettings in app.config to run this sample");
}
var authority = String.Format("{0}{1}", _environment.Endpoints[AzureEnvironment.Endpoint.ActiveDirectory], _TenantId);
var authContext = new AuthenticationContext(authority);
var credential = new ClientCredential(_ClientId, _ClientKey);
var authResult = await authContext.AcquireTokenAsync(_environment.Endpoints[AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId], credential);
return new TokenCloudCredentials(_SubscriptionId, authResult.AccessToken);
}
static void AddCustomDomainToSite(string sDomainName)
{
Domain domain = new Domain();
_websiteClient.Domains.CreateOrUpdateAsync(_ResourceGroupName, "mycustomdomain.com", domain);
}
I am trying to add mycustomdomain.com to my Azure app service. When I execute the code _websiteClient.Domains.CreateOrUpdateAsync(_ResourceGroupName, "mycustomdomain.com", domain);, nothing happens. I do not get any errors, and I do not see the custom domain listed under Custom Domains in my app service.
I have already verified ownership of the domain, and I can add it to my app service via the portal, but I am trying to add it through C#. Can someone please help me?
Did you already check on "_websiteClient.WebApps.CreateOrUpdateHostNameBindingAsync(resourcegroup, appservicename, hostname, binding)"?
The binding i give as parameter is this one:
var binding = new HostNameBinding{CustomHostNameDnsRecordType = CustomHostNameDnsRecordType.CName};
For me this is working to add a custom domain to the app service.
Im having some problems retriving data from sharepoint (Disks) for a dotnet core app.
At the moment my app tries to use the app itself, and not the logged in user to retrive disks, but the prefered way would be to use the accesstoken for the logged in user instead.
Maybe authenticating as the app with clientId and secret wont work with drives at all?
The login works fine.
I've set up a dotnet core app with the following startup:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(30);
})
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
I also have the following services registered:
services.AddTransient<IAuthenticationProvider, GraphAuthenticationProvider>();
services.AddTransient<IGraphServiceClient, GraphServiceClient>();
services.AddTransient<IGraphProvider, MicrosoftGraphProvider>();
where i use the this to authenticate:
public class GraphAuthenticationProvider : IAuthenticationProvider
{
public const string GRAPH_URI = "https://graph.microsoft.com/";
private string _tenantId { get; set; }
private string _clientId { get; set; }
private string _clientSecret { get; set; }
public GraphAuthenticationProvider(IConfiguration configuration)
{
_tenantId = configuration.GetValue<string>("AzureAd:TenantId");
_clientId = configuration.GetValue<string>("AzureAd:ClientId");
_clientSecret = configuration.GetValue<string>("AzureAd:ClientSecret");
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
AuthenticationContext authContext = new AuthenticationContext($"https://login.microsoftonline.com/{_tenantId}");
ClientCredential creds = new ClientCredential(_clientId, _clientSecret);
//I have tried using acquireTokensAsync with scopes, but there is no such method.
AuthenticationResult authResult = await authContext.AcquireTokenAsync(GRAPH_URI, creds);
request.Headers.Add("Authorization", "Bearer " + authResult.AccessToken);
}
}
I have given the app plenty of permissions in the API settings in portal, mostly because im unsure what i need, and at the moment im just eager to make it work first, then refactor some.
The app is able to log in, and retrive the following data with the SDK:
var groups = await _graphServiceClient.Groups[appSettings.AzureAd.GroupId].Request().GetAsync();
however: the following does not work:
var groupDrives = await _graphServiceClient.Groups[appSettings.AzureAd.GroupId].Drives
.Request()
.GetAsync();
and i get the following error:
Code: AccessDenied
Message: Either scp or roles claim need to be present in the token.
I also have user login in startup, and the app wont be used without logging in towards azure AD:
Could i use the accessToken for the user instead?
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters = new TokenValidationParameters() { NameClaimType = "name" };
options.TokenValidationParameters.ValidateIssuer = false;
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = async ctx =>
{
var roleGroups = new Dictionary<string, string>();
Configuration.Bind("AuthorizationGroups", roleGroups);
var clientApp = ConfidentialClientApplicationBuilder
.Create(Configuration["AzureAD:ClientId"])
.WithTenantId(Configuration["AzureAD:TenantId"])
.WithClientSecret(Configuration["AzureAD:ClientSecret"])
.Build();
var authResult = await clientApp
.AcquireTokenOnBehalfOf(new[] { "User.Read", "Group.Read.All" }, new UserAssertion(ctx.SecurityToken.RawData))
.ExecuteAsync();
var graphClient = new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider(async (requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authResult.AccessToken);
}));
//Could i register the graphservice as a singelton with the users accesstoken?
//Fetching drives here with the accessToken from user works.
var graphService = new GraphService(graphClient, Configuration);
var memberGroups = await graphService.CheckMemberGroupsAsync(roleGroups.Keys);
var claims = memberGroups.Select(groupGuid => new Claim(ClaimTypes.Role, roleGroups[groupGuid]));
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
}
};
});
I would actually like to use the users accesstoken to retrive the drives etc, but im not sure on how to store\reuse the accesstoken. I should probably register the service as a singelton with the users accesstoken as mentioned in the comment?
I followed this guide, and it has the same classes\services i have used:
http://www.keithmsmith.com/get-started-microsoft-graph-api-calls-net-core-3/
I actually thought the option on top here was just a header. It might be easier now.. https://i.imgur.com/yfZWaoe.png
it feels like you are mixing up a whole bunch of concepts here. that example you are using is based on the client credentials flow. you should probably start by reading up on the different types of authentication flows available. https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-authentication-flows
In general when you use the client credential flow, the permissions you need to set are application permissions in the api permissions blade. Delegated permissions are for user login flows.
when you are using delegated permissions like you are above. and you use a flow that gets user tokens, then the access that the application has is based on the access the user has. for example, if you delegate groups.read.all with delegated permissions, then that gives the application access to read all the groups that That specific user has access to. it doesn't give the application access to all groups. if this is what you want, then by all means use the user flow.
You didn't mention if you were writing a web app, or what, but if you are you may want to look carefully at the on-behalf-of flow. here is an example of it. https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/2-WebApp-graph-user/2-1-Call-MSGraph
but again above applies for the permissions, when you get a user token your app will only have access to the items that user has access to. no more. eg user A has access to sharepoint site A, user B has no access to site A, when you use a user token for user B to call graph it will not return results for site A since user B does not have access to it.
You've defined Delegated scopes but are attempting to authenticate using Client Credentials. Delegated scopes are named such because the User is delegating their access to your application.
You need to request Application scopes when authenticating without a User.
I need to connect a Google Calendar on my .NET 4.5 application (VS 2013 project).
I want to get all the information from the Calendar, such as: events, dates, notes, names, guests, etc...
I used the Google Developer Console to create both a Web Application Client ID and a Service Account, but I get different errors and no results.
I've implemented 2 different methods, one to login with Web Application Client ID and one to use Service Account.
This is the common ASPX page
public partial class Calendar : System.Web.UI.Page
{
// client_secrets.json path.
private readonly string GoogleOAuth2JsonPath = ConfigurationManager.AppSettings["GoogleOAuth2JsonPath"];
// p12 certificate path.
private readonly string GoogleOAuth2CertificatePath = ConfigurationManager.AppSettings["GoogleOAuth2CertificatePath"];
// #developer... e-mail address.
private readonly string GoogleOAuth2EmailAddress = ConfigurationManager.AppSettings["GoogleOAuth2EmailAddress"];
// certificate password ("notasecret").
private readonly string GoogleOAuth2PrivateKey = ConfigurationManager.AppSettings["GoogleOAuth2PrivateKey"];
// my Google account e-mail address.
private readonly string GoogleAccount = ConfigurationManager.AppSettings["GoogleAccount"];
protected void Page_Load(object sender, EventArgs e)
{
// Enabled one at a time to test
//GoogleLoginWithServiceAccount();
GoogleLoginWithWebApplicationClientId();
}
}
Using Web Application Client ID
I've tried to configure the redirect URIs parameter for the JSON config file, but no URI seems to work. I'm on development environment so I'm using IIS Express on port 44300 (SSL enabled). The error I'm getting is:
Error: redirect_uri_mismatch
Application: CalendarTest
The redirect URI in the request: http://localhost:56549/authorize/ did not match a registered redirect URI.
Request details
scope=https://www.googleapis.com/auth/calendar
response_type=code
redirect_uri=http://localhost:56549/authorize/
access_type=offline
client_id=....apps.googleusercontent
The code
private void GoogleLoginWithWebApplicationClientId()
{
UserCredential credential;
// This example uses the client_secrets.json file for authorization.
// This file can be downloaded from the Google Developers Console
// project.
using (FileStream json = new FileStream(Server.MapPath(GoogleOAuth2JsonPath), FileMode.Open,
FileAccess.Read))
{
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(json).Secrets,
new[] { CalendarService.Scope.Calendar },
"...#developer.gserviceaccount.com", CancellationToken.None,
new FileDataStore("Calendar.Auth.Store")).Result;
}
// Create the service.
CalendarService service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "CalendarTest"
});
try
{
CalendarListResource.ListRequest listRequest = service.CalendarList.List();
IList<CalendarListEntry> calendarList = listRequest.Execute().Items;
foreach (CalendarListEntry entry in calendarList)
{
txtCalendarList.Text += "[" + entry.Summary + ". Location: " + entry.Location + ", TimeZone: " +
entry.TimeZone + "] ";
}
}
catch (TokenResponseException tre)
{
txtCalendarList.Text = tre.Message;
}
}
Using Service Account (preferred)
I can reach the CalendarListResource.ListRequest listRequest = service.CalendarList.List(); line, so I guess the login works, but then, when I want the list on IList<CalendarListEntry> calendarList = listRequest.Execute().Items; I get the following error:
Error:"unauthorized_client", Description:"Unauthorized client or scope in request.", Uri:""
The code
private void GoogleLoginWithServiceAccount()
{
/*
* From https://developers.google.com/console/help/new/?hl=en_US#generatingoauth2:
* The name of the downloaded private key is the key's thumbprint. When inspecting the key on your computer, or using the key in your application,
* you need to provide the password "notasecret".
* Note that while the password for all Google-issued private keys is the same (notasecret), each key is cryptographically unique.
* GoogleOAuth2PrivateKey = "notasecret".
*/
X509Certificate2 certificate = new X509Certificate2(Server.MapPath(GoogleOAuth2CertificatePath),
GoogleOAuth2PrivateKey, X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(GoogleOAuth2EmailAddress)
{
User = GoogleAccount,
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
// Create the service.
CalendarService service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "CalendarTest"
});
try
{
CalendarListResource.ListRequest listRequest = service.CalendarList.List();
IList<CalendarListEntry> calendarList = listRequest.Execute().Items;
foreach (CalendarListEntry entry in calendarList)
{
txtCalendarList.Text += "[" + entry.Summary + ". Location: " + entry.Location + ", TimeZone: " +
entry.TimeZone + "] ";
}
}
catch (TokenResponseException tre)
{
txtCalendarList.Text = tre.Message;
}
}
I prefer the Service Account login because there's no need for user to login with consent screen, since the application should do it by itself each time it needs to refresh. Is it possible to use a Service Account with free Google Account or do I need Admin console? I've read many conflicting reports about that...
Anyway, looking around with Google and also in StackOverflow, I didn't find a solution. I've seen and tried many questions and solutions but with no results. Some examples:
Keep getting Error: redirect_uri_mismatch using youtube api v3
Google Calendar redirect_uri_mismatch
Google API Calender v3 Event Insert via Service Account using Asp.Net MVC
https://developers.google.com/google-apps/calendar/instantiate
https://groups.google.com/forum/#!topic/google-calendar-api/MySzyAXq12Q
Please help! :-)
UPDATE 1 - Using Service Account (preferred) - SOLVED!
The only problem in my code was:
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(GoogleOAuth2EmailAddress)
{
//User = GoogleAccount,
Scopes = new[] { CalendarService.Scope.Calendar }
}.FromCertificate(certificate));
There's NO NEED for User = GoogleAccount.
There is definitely something wrong with your authentication. Here is a copy of my Service account authentication method.
/// <summary>
/// Authenticating to Google using a Service account
/// Documentation: https://developers.google.com/accounts/docs/OAuth2#serviceaccount
/// </summary>
/// <param name="serviceAccountEmail">From Google Developer console https://console.developers.google.com</param>
/// <param name="keyFilePath">Location of the Service account key file downloaded from Google Developer console https://console.developers.google.com</param>
/// <returns></returns>
public static CalendarService AuthenticateServiceAccount(string serviceAccountEmail, string keyFilePath)
{
// check the file exists
if (!File.Exists(keyFilePath))
{
Console.WriteLine("An Error occurred - Key file does not exist");
return null;
}
string[] scopes = new string[] {
CalendarService.Scope.Calendar , // Manage your calendars
CalendarService.Scope.CalendarReadonly // View your Calendars
};
var certificate = new X509Certificate2(keyFilePath, "notasecret", X509KeyStorageFlags.Exportable);
try
{
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes
}.FromCertificate(certificate));
// Create the service.
CalendarService service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Calendar API Sample",
});
return service;
}
catch (Exception ex)
{
Console.WriteLine(ex.InnerException);
return null;
}
}
I have a tutorial on it as well. My tutorial Google Calendar API Authentication with C# The code above was ripped directly from my sample project Google-Dotnet-Samples project on GitHub
Note/headsup: Remember that a service account isn't you. It does now have any calendars when you start you need to create a calendar and insert it into the calendar list before you are going to get any results back. Also you wont be able to see this calendar though the web version of Google Calendar because you cant log in as a service account. Best work around for this is to have the service account grant you permissions to the calendar.
It seems very much that the current version of LiveAuthClient is either broken or something in my setup/configuration is. I obtained LiveSDK version 5.4.3499.620 via Package Manager Console.
I'm developing an ASP.NET application and the problem is that the LiveAuthClient-class seems to not have the necessary members/events for authentication so it's basically unusable.
Notice that InitializeAsync is misspelled aswell.
What's wrong?
UPDATE:
I obtained another version of LiveSDK which is for ASP.NET applications but now I get the exception "Could not find key with id 1" everytime I try either InitializeSessionAsync or ExchangeAuthCodeAsync.
https://github.com/liveservices/LiveSDK-for-Windows/issues/3
I don't think this is a proper way to fix the issue but I don't have other options at the moment.
I'm a little late to the party, but since I stumbled across this trying to solve what I assume is the same problem (authenticating users with Live), I'll describe how I got it working.
First, the correct NuGet package for an ASP.NET project is LiveSDKServer.
Next, getting user info is a multi-step process:
Send the user to Live so they can authorize your app to access their data (the extent of which is determined by the "scopes" you specify)
Live redirects back to you with an access code
You then request user information using the access code
This is described fairly well in the Live SDK documentation, but I'll include my very simple working example below to put it all together. Managing tokens, user data, and exceptions is up to you.
public class HomeController : Controller
{
private const string ClientId = "your client id";
private const string ClientSecret = "your client secret";
private const string RedirectUrl = "http://yourdomain.com/home/livecallback";
[HttpGet]
public ActionResult Index()
{
// This is just a page with a link to home/signin
return View();
}
[HttpGet]
public RedirectResult SignIn()
{
// Send the user over to Live so they can authorize your application.
// Specify whatever scopes you need.
var authClient = new LiveAuthClient(ClientId, ClientSecret, RedirectUrl);
var scopes = new [] { "wl.signin", "wl.basic" };
var loginUrl = authClient.GetLoginUrl(scopes);
return Redirect(loginUrl);
}
[HttpGet]
public async Task<ActionResult> LiveCallback(string code)
{
// Get an access token using the authorization code
var authClient = new LiveAuthClient(ClientId, ClientSecret, RedirectUrl);
var exchangeResult = await authClient.ExchangeAuthCodeAsync(HttpContext);
if (exchangeResult.Status == LiveConnectSessionStatus.Connected)
{
var connectClient = new LiveConnectClient(authClient.Session);
var connectResult = await connectClient.GetAsync("me");
if (connectResult != null)
{
dynamic me = connectResult.Result;
ViewBag.Username = me.name; // <-- Access user info
}
}
return View("Index");
}
}