I have created a simple ASP.Net web forms application and want to upload a file to One Drive. I implemented MS Graph APIs for this purpose. There are three files, Upload.aspx, Upload.aspx.cs, and MsalAuthentication.cs (The code is also given below). When I click on "Upload button" and the control goes to:
var result = await _clientApplication.AcquireTokenByUsernamePassword(_scopes, _username, _password).ExecuteAsync();, it stucks here and doesn't move to the next statement.
In web.Config file, I have also given applicationId and tenantId as:
< appSettings >
< add key="tenantId" value="some id" />
< add key="applicationId" value="some id" />
< /appSettings>
Can anybody tell me about the issue?
The code is given below
Upload.aspx.cs
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Security;
using Microsoft.Identity.Client;
using Microsoft.Graph;
using Microsoft.Extensions.Configuration;
using Helpers;
using System.Configuration;
using System.Collections.Specialized;
namespace WebFormsOneDrive
{
public partial class Upload : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
var config = LoadAppSettings();
if (config == null)
{
Console.WriteLine("Invalid appsettings.json file.");
return;
}
var userName = ReadUsername();
var userPassword = ReadPassword();
var client = GetAuthenticatedGraphClient(config, userName, userPassword);
// request 1 - upload small file to user's onedrive
var fileName = FileUpload1.FileName;
var filePath = Path.Combine(#"D:\webform\upload\", fileName);
FileStream fileStream = new FileStream(filePath, FileMode.Open);
var uploadedFile = client.Me.Drive.Root
.ItemWithPath(fileName)
.Content
.Request()
.PutAsync<DriveItem>(fileStream)
.Result;
Console.WriteLine("File uploaded to: " + uploadedFile.WebUrl);
}
private static NameValueCollection LoadAppSettings()
{
try
{
//var config = new ConfigurationBuilder()
// .SetBasePath(System.IO.Directory.GetCurrentDirectory())
// .AddXMLFile("Web.config", false, true)
// .Build();
var config = ConfigurationManager.GetSection("appSettings") as NameValueCollection;
if (string.IsNullOrEmpty(config["applicationId"]) ||
string.IsNullOrEmpty(config["tenantId"]))
{
return null;
}
return config;
}
catch (System.IO.FileNotFoundException)
{
return null;
}
}
private static IAuthenticationProvider CreateAuthorizationProvider(NameValueCollection config, string userName, SecureString userPassword)
{
var clientId = config["applicationId"];
var authority = $"https://login.microsoftonline.com/{config["tenantId"]}/v2.0";
List<string> scopes = new List<string>();
scopes.Add("User.Read");
scopes.Add("Files.Read");
scopes.Add("Files.ReadWrite");
var cca = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(authority)
.Build();
return MsalAuthenticationProvider.GetInstance(cca, scopes.ToArray(), userName, userPassword);
}
private static GraphServiceClient GetAuthenticatedGraphClient(NameValueCollection config, string userName, SecureString userPassword)
{
var authenticationProvider = CreateAuthorizationProvider(config, userName, userPassword);
var graphClient = new GraphServiceClient(authenticationProvider);
return graphClient;
}
private static SecureString ReadPassword()
{
//Console.WriteLine("Enter your password");
//SecureString password = new SecureString();
//while (true)
//{
// ConsoleKeyInfo c = Console.ReadKey(true);
// if (c.Key == ConsoleKey.Enter)
// {
// break;
// }
// password.AppendChar(c.KeyChar);
// Console.Write("*");
//}
//Console.WriteLine();
var password = new SecureString();
password.AppendChar('p');
password.AppendChar('a');
password.AppendChar('s');
password.AppendChar('s');
password.AppendChar('w');
password.AppendChar('o');
password.AppendChar('r');
password.AppendChar('d');
return password;
}
private static string ReadUsername()
{
//string username;
//Console.WriteLine("Enter your username");
//username = Console.ReadLine();
//return username;
string userName = "abcd#domain#onmicrosoft.com";
return userName;
}
}
}
MsalAuthentication.cs
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using Microsoft.Graph;
namespace Helpers
{
public class MsalAuthenticationProvider : IAuthenticationProvider
{
private static MsalAuthenticationProvider _singleton;
private IPublicClientApplication _clientApplication;
private string[] _scopes;
private string _username;
private SecureString _password;
private string _userId;
private MsalAuthenticationProvider(IPublicClientApplication clientApplication, string[] scopes, string username, SecureString password)
{
_clientApplication = clientApplication;
_scopes = scopes;
_username = username;
_password = password;
_userId = null;
}
public static MsalAuthenticationProvider GetInstance(IPublicClientApplication clientApplication, string[] scopes, string username, SecureString password)
{
if (_singleton == null)
{
_singleton = new MsalAuthenticationProvider(clientApplication, scopes, username, password);
}
return _singleton;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var accessToken = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
}
public async Task<string> GetTokenAsync()
{
if (!string.IsNullOrEmpty(_userId))
{
try
{
var account = await _clientApplication.GetAccountAsync(_userId);
if (account != null)
{
var silentResult = await _clientApplication.AcquireTokenSilent(_scopes, account).ExecuteAsync();
return silentResult.AccessToken;
}
}
catch (MsalUiRequiredException) { }
}
var result = await _clientApplication.AcquireTokenByUsernamePassword(_scopes, _username, _password).ExecuteAsync();
_userId = result.Account.HomeAccountId.Identifier;
return result.AccessToken;
}
I think you mixed 2 concepts:
access on behalf of a user's connection (via AAD for example)
access via the security of an application (client secret)
In general we cannot pass the password of a user in clear like that to an API, we play with tokens.
The Graph Doc for all scenarios
Add a client secret to your AAD and give all the roles base you need to your api.
If you use Credential flow, you should not use "Me" in the graph call, but something like : graph.Users["user#email.com"].Drive....
Otherwise if you realy want to use password you can do that :
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.Build();
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
User me = await graphClient.Me.Request()
.WithUsernamePassword(email, password)
.GetAsync();
Related
I need to silently authenticate in Azure Blob Storage from a .NET application running on a Windows machine that is domain-joined and the domain is synced to Azure AD.
I am using this example of authentication flow as the base and trying to adapt it for Blob Storage. I successfully obtain a token from AcquireTokenByIntegratedWindowsAuth() method of PublicClientApplication, but I cannot figure out how to supply it to BlobContainerClient. The most appropriate constructor seems to be the one accepting TokenCredential, but I cannot find a suitable class among descendants of TokenCredential.
I ended up writing my own implementation of TokenCredential:
internal class IwaCredential : TokenCredential
{
private readonly IPublicClientApplication _application;
private readonly string[] _scopes;
public IwaCredential(IPublicClientApplication app, string[] scopes)
{
_application = app;
_scopes = scopes;
}
private async Task<AuthenticationResult> AuthenticateAsync()
{
AuthenticationResult? result = null;
var accounts = await _application.GetAccountsAsync();
if (accounts.Any())
{
try
{
result = await _application.AcquireTokenSilent(_scopes, accounts.FirstOrDefault()).ExecuteAsync();
}
catch (MsalUiRequiredException)
{
}
}
if (result == null)
{
result = await _application.AcquireTokenByIntegratedWindowsAuth(_scopes).ExecuteAsync();
}
return result;
}
private async Task<AccessToken> GetAccessTokenAsync()
{
var authResult = await AuthenticateAsync();
return new AccessToken(authResult.AccessToken, authResult.ExpiresOn);
}
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
return GetAccessTokenAsync().GetAwaiter().GetResult();
}
public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
return new ValueTask<AccessToken>(GetAccessTokenAsync());
}
}
Then I am able to pass instance of that to the client:
var appOptions = new PublicClientApplicationOptions
{
ClientId = "...",
TenantId = "...",
};
var app = PublicClientApplicationBuilder.CreateWithApplicationOptions(appOptions).Build();
var cred = new IwaCredential(app, new string[] { "https://storage.azure.com/user_impersonation" });
var client = new BlobContainerClient(new Uri("https://foobar.blob.core.windows.net/upload"), cred);
using (Stream file = new FileStream(#"C:\Windows\win.ini", FileMode.Open, FileAccess.Read))
{
var res = await client.UploadBlobAsync("prefix/win.ini", file);
Console.WriteLine(res);
}
It works, but I still feel like I am missing something as I believe there should be support for that flow within the standard library.
Am I doing it right way? Please suggest improvements.
Why not this method using new DefaultAzureCredential(includeInteractiveCredentials: true).
BlobContainerClient blobContainerClient = new BlobContainerClient(
new Uri(#"https://your-blob-uri.blob.core.windows.net/your-container")
, new DefaultAzureCredential(includeInteractiveCredentials: true));
then pass that BlobContainerClient to UploadFile
public static async Task UploadFile
(BlobContainerClient containerClient, string localFilePath)
{
string fileName = Path.GetFileName(localFilePath);
BlobClient blobClient = containerClient.GetBlobClient(fileName);
await blobClient.UploadAsync(localFilePath, true);
}
{"odata.error":{"code":"Authorization_RequestDenied",
"message":
{"lang":"en","value":"Insufficient privileges to complete the operation."},
"requestId":"b205e5d0-f929-418e-9153-f1994e2c0893",
"date":"2020-02-15T06:53:57"}
}
I am able to retrieve the authentication token from the server and have granted all the permissions through the AAD but still I'm facing the same issue.
Would be great if someone could help me out.
I am using the Microsoft Graph API.
Below is the code that I am using
private const string clientID = "XXXX";
private const string addInstance = "https://login.microsoftonline.com/{0}";
private const string tenant = "XYZ";
private const string resource = "https://graph.windows.net";
private const string appKey = "appkey";
static string authority = String.Format(CultureInfo.InvariantCulture, addInstance, tenant);
private static HttpClient httpclient = new HttpClient();
private static AuthenticationContext context = null;
private static ClientCredential credential = null;
static void Main(string[] args)
{
context = new AuthenticationContext(authority);
credential = new ClientCredential(clientID,appKey);
Task<string> token = GetToken();
token.Wait();
Console.WriteLine(token.Result);
Task<string> users = GetUsers(token.Result);
users.Wait();
Console.WriteLine(users.Result);
//Console.ReadLine();
}
private static async Task<string> GetUsers(string result)
{
string users = null;
string queryString = "api-version=1.6";
var uri = "https://graph.windows.net/ *The Microsoft 365 account assosciated with the tenant* /users?"+ queryString;
httpclient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result);
var getResult = await httpclient.GetAsync(uri);
if (getResult != null)
{
users = await getResult.Content.ReadAsStringAsync();
}
return users;
}
private static async Task<string> GetToken()
{
AuthenticationResult result = null;
string token = null;
result = await context.AcquireTokenAsync(resource, credential);
token = result.AccessToken;
return token;
}
}
I have tried following way and perfectly worked for me.
//Token Request End Point
string tokenUrl = $"https://login.microsoftonline.com/yourTenant.onmicrosoft.com/oauth2/token";
var tokenRequest = new HttpRequestMessage(HttpMethod.Post, tokenUrl);
//I am Using client_credentials as It is mostly recommended
tokenRequest.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = "b603c7be-a956_Your_Client_Id_a45996-e6921e61f36955",
["client_secret"] = "Vxf1SluKbgu4PF0loj_Your_Client_Secret_okjh8wL/yujh45lojhgg=",
["resource"] = "https://graph.windows.net"
});
dynamic json;
AccessTokenClass results = new AccessTokenClass();
HttpClient client = new HttpClient();
var tokenResponse = await client.SendAsync(tokenRequest);
json = await tokenResponse.Content.ReadAsStringAsync();
results = JsonConvert.DeserializeObject<AccessTokenClass>(json);
//New Block For Accessing Data from Microsoft Graph Rest API
HttpClient _client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, string.Format("https://graph.windows.net/YourTenant.onmicrosoft.com/users?api-version=1.6"));
//Passing Token For this Request
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", results.access_token);
//Check The Response and extract response data
HttpResponseMessage response = await _client.SendAsync(request);
dynamic objGpraphUserList = JsonConvert.DeserializeObject<dynamic>(await response.Content.ReadAsStringAsync());
return objGpraphUserList
Class Used:
public class AccessTokenClass
{
public string token_type { get; set; }
public string expires_in { get; set; }
public string resource { get; set; }
public string access_token { get; set; }
}
I got the User List as expected. See the screen shot.
Validate Your Token :
Check your token on https://jwt.io/ which should have User.ReadWrite.All or User.Read.All Application permission
Note:
You should have following permission on Azure Active Directory Graph
For more information please refer to this official document
Hope this would help.
Here is my code which is pulling only 100 users from the active directory. I have granted the "read all user profile permission" in application and delegated sections as well.
namespace MVCDemoGraphAPI.Controllers
{
public class HomeController : Controller
{
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
private static string appKey = ConfigurationManager.AppSettings["ida:AppKey"];
public async Task<string> Users()
{
string authority = string.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
AuthenticationContext authContext = new AuthenticationContext(authority);
AuthenticationResult result = null;
try
{
result = await authContext.AcquireTokenAsync("https://graph.microsoft.com",
new ClientCredential(clientId, appKey));
}
catch (Exception)
{
throw;
}
//Now call the Graph API
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/users");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
string output = await response.Content.ReadAsStringAsync();
return output;
}
}
}
You have to use Paging filters as described in here:
https://learn.microsoft.com/en-us/previous-versions/azure/ad/graph/howto/azure-ad-graph-api-supported-queries-filters-and-paging-options ,
mainly paging forward.
I recommend to use c# graph client Nuget and then use the code below:
var users = await graphClient.Users.Request().GetAsync();
try
{
while (users != null)
{
var usersList = users.CurrentPage.ToList();
count = count + usersList.Count();
users = await users.NextPageRequest.GetAsync();
}
}
catch
{
//
}
I am trying to access the Microsoft Graph API to obtain a user’s outlook groups.
Here is the code to retrieve the access token:
public static async Task<string> GetGraphAccessTokenAsync()
{
string AzureAdGraphResourceURL = "https://graph.microsoft.com/";
string signedInUserUniqueName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var userObjectId = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var clientCredential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.AppKey);
var userIdentifier = new UserIdentifier(userObjectId, UserIdentifierType.UniqueId);
AuthenticationContext authContext = new AuthenticationContext(
SettingsHelper.Authority, new ADALTokenCache(signedInUserUniqueName));
var result = await authContext.AcquireTokenSilentAsync(AzureAdGraphResourceURL, clientCredential, userIdentifier);
return result.AccessToken;
}
The method uses a settings helper as follows:
public class SettingsHelper
{
private static string _clientId = ConfigurationManager.AppSettings["ida:ClientID"];
private static string _appKey = ConfigurationManager.AppSettings["ida:Password"];
private static string _tenantId = ConfigurationManager.AppSettings["ida:TenantID"];
private static string _authorizationUri = "https://login.windows.net";
private static string _authority = "https://login.windows.net/{0}/";
private static string _graphResourceId = "https://graph.windows.net";
public static string ClientId
{
get
{
return _clientId;
}
}
public static string AppKey
{
get
{
return _appKey;
}
}
public static string TenantId
{
get
{
return _tenantId;
}
}
public static string AuthorizationUri
{
get
{
return _authorizationUri;
}
}
public static string Authority
{
get
{
return String.Format(_authority, _tenantId);
}
}
public static string AADGraphResourceId
{
get
{
return _graphResourceId;
}
}
}
This is the error that I get:
Failed to acquire token silently. Call method AcquireToken
Exception Details:
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalSilentTokenAcquisitionException : Failed to acquire token silently. Call method AcquireToken
The error occurs specifically at this line:
var result = await authContext.AcquireTokenSilentAsync(AzureAdGraphResourceURL, clientCredential, userIdentifier);
I have checked to ensure that the UserIdentifier matches the value in the cache, but it stills seems to reject the token. Any ideas of where I might be going wrong?
Firs of all, make sure to use the Microsoft graph endpoint (actually you used the Active directory endpoint)
private static readonly string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static readonly string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
private static readonly string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
private static readonly string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
private static readonly string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
private static readonly string graphResourceId = "https://graph.microsoft.com";
private static readonly Uri graphEndpointId = new Uri("https://graph.microsoft.com/v1.0/");
Before making a Silent call, you have to make a classic call by retrieving a code. I assume you're in an MVC application.
Here is my Startup.Auth.cs code :
public void ConfigureAuth(IAppBuilder app)
{
ApplicationDbContext db = new ApplicationDbContext();
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = AuthenticationHelper.ClientId,
Authority = AuthenticationHelper.AadInstance + AuthenticationHelper.TenantId,
PostLogoutRedirectUri = AuthenticationHelper.PostLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications()
{
// If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
AuthorizationCodeReceived =async (context) =>
{
var code = context.Code;
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
try
{
var result = await AuthenticationHelper.GetAccessTokenByCodeAsync(signedInUserID, code);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
//throw;
}
}
}
});
}
Here is the code I used in my AuthenticationHelper class :
public async static Task<AuthenticationResult> GetAccessTokenByCodeAsync(string signedInUserID, string code)
{
ClientCredential credential = new ClientCredential(clientId, appKey);
AuthenticationContext authContext = new AuthenticationContext(AadInstance + TenantId, new ADALTokenCache(signedInUserID));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);
return result;
}
and Then, each time I need to make a request to the graph, here is the code I used to get a token :
public async static Task<string> GetTokenForApplicationAsync()
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
// get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
ClientCredential clientcred = new ClientCredential(clientId, appKey);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's database
AuthenticationContext authenticationContext = new AuthenticationContext(AadInstance + TenantId, new ADALTokenCache(signedInUserID));
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(GraphResourceId, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
return authenticationResult.AccessToken;
}
An other thhings to notice : Make sure your tenantId is the guid of your tenant. For some reason, sometimes, if you use your tenant name, adal make a difference and could raise this kind of error.
I am using this Link as a starting point as i am new to Asp.net MVC as such.
I have been able to get the data of the facebook users what permissions should i use to get the users Email ID and where?
dynamic me = client.Get("me");
if (response.ContainsKey("verified"))
{
facebookVerified = response["verified"];
}
else
{
facebookVerified = false;
}
db.ExternalUsers.Add(new ExternalUserInformation
{
UserId = newUser.UserId,
FullName = me.name,
Link = me.link,
Email = model.Email, // Want the Email ID from Facebook
Gender = me.gender,
Verified = facebookVerified
});
LOGIN CODE:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
What you are missing here is getting additional permission for getting the email address from facebook.
See the below two screenshot, second screenshot requests for additional information including email.
Basic Permission
More Permissions
To do that you need to this additional required info as "scope".
I did a little tutorial on how to login with facebook today and can be read here - Using Facebook Login with ASP.NET MVC 4. This will answer most of your queries.
For your question here is what you should do:
Create a FacebookScopedClient class (code below) and then in your AuthConfig.cs use it like this
var facebooksocialData = new Dictionary<string, object>();
facebooksocialData.Add("scope", "email, publish_stream, read_stream");
OAuthWebSecurity.RegisterClient(new FacebookScopedClient(
appId: "xxxxxxxx",
appSecret: "xxxxxxxxxxxxxxxxxxx",
scope:"email, user_likes, friends_likes, user_birthday),
"Facebook",
null
);
Code for FacebookScopedClient class -
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using DotNetOpenAuth.AspNet;
using Newtonsoft.Json;
public class FacebookScopedClient : IAuthenticationClient
{
private string appId;
private string appSecret;
private string scope;
private const string baseUrl = "https://www.facebook.com/dialog/oauth?client_id=";
public const string graphApiToken = "https://graph.facebook.com/oauth/access_token?";
public const string graphApiMe = "https://graph.facebook.com/me?";
private static string GetHTML(string URL)
{
string connectionString = URL;
try
{
System.Net.HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(connectionString);
myRequest.Credentials = CredentialCache.DefaultCredentials;
//// Get the response
WebResponse webResponse = myRequest.GetResponse();
Stream respStream = webResponse.GetResponseStream();
////
StreamReader ioStream = new StreamReader(respStream);
string pageContent = ioStream.ReadToEnd();
//// Close streams
ioStream.Close();
respStream.Close();
return pageContent;
}
catch (Exception)
{
}
return null;
}
private IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
{
string token = GetHTML(graphApiToken + "client_id=" + appId + "&redirect_uri=" + HttpUtility.UrlEncode(redirectURI) + "&client_secret=" + appSecret + "&code=" + accessCode);
if (token == null || token == "")
{
return null;
}
string access_token = token.Substring(token.IndexOf("access_token="), token.IndexOf("&"));
string data = GetHTML(graphApiMe + "fields=id,name,email,username,gender,link&" + access_token);
// this dictionary must contains
Dictionary<string, string> userData = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
return userData;
}
public FacebookScopedClient(string appId, string appSecret, string scope)
{
this.appId = appId;
this.appSecret = appSecret;
this.scope = scope;
}
public string ProviderName
{
get { return "Facebook"; }
}
public void RequestAuthentication(System.Web.HttpContextBase context, Uri returnUrl)
{
string url = baseUrl + appId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=" + scope;
context.Response.Redirect(url);
}
public AuthenticationResult VerifyAuthentication(System.Web.HttpContextBase context)
{
string code = context.Request.QueryString["code"];
string rawUrl = context.Request.Url.OriginalString;
//From this we need to remove code portion
rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
IDictionary<string, string> userData = GetUserData(code, rawUrl);
if (userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
string id = userData["id"];
string username = userData["username"];
userData.Remove("id");
userData.Remove("username");
AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
return result;
}
}
References:
Facebook web application extended permissions second step dont show second-step-dont-show/18904735#18904735
Authenticating Facebook users with MVC 4 OAuth AND obtaining Scope Permissions!