Microsoft Graph giving an error: Authentication Challenge is required - sdk

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> UploadExcel(HttpPostedFileBase upload)
{
//Check File code here//
DriveItem item = null;
string Secret1 = "secret";
string Client1 = "clientid";
string Tenant1 = "tenantid";
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(Client1)
.WithAuthority(AzureCloudInstance.AzurePublic, Tenant1)
.WithRedirectUri("uri")
.WithClientSecret(Secret1)
.Build();
AuthorizationCodeProvider authProvider1 = new AuthorizationCodeProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider1);
using (Stream file1 = upload.InputStream)
{
try
{
item = await graphClient.Me.Drive.Items["id_of_a_file"]
.Content
.Request()
.PutAsync<DriveItem>(file1);
}
catch (Exception ex)
{
//Handle Exception
}
}
if (item == null)
{
//Tell user
}
return RedirectToAction("ActionName");
}
Also Redirect URI is always null no matter what I do.
I have searched elsewhere but could not find a solution. This is a Web application. User login is handled by us. Users upload files to Company's OneDrive Account. All the necessary permissions and admin consent are granted. Still error is not resolved.

Related

ASP.Net 6 Core CSOM ExecuteQuery when getting a non-available user in SPO

In asp.net core 6 and CSOM library, I'm trying add a permission to a SPO file as following code.
public async Task<IActionResult> AddPermission(Guid guid, String[] emailList)
{
using (var authenticationManager = new AuthenticationManager())
using (var context = authenticationManager.GetContext(_site, _user, _securePwd))
{
File file= context.Web.GetFileById(guid);
SetFilePermission(context, file, emailList);
file.ListItemAllFields.SystemUpdate();
context.Load(file.ListItemAllFields);
await context.ExecuteQueryAsync();
return NoContent();
}
}
private static void SetFilePermission(ClientContext context, File file, string[] emailList)
{
if (emailList != null)
{
file.ListItemAllFields.BreakRoleInheritance(true, false);
var role = new RoleDefinitionBindingCollection(context);
role.Add(context.Web.RoleDefinitions.GetByType(permissionLevel));
foreach (string email in emailList)
{
User user = context.Web.SiteUsers.GetByEmail(email);
file.ListItemAllFields.RoleAssignments.Add(user, role);
}
}
}
This work successfully if only the user is available in SPO, or exception occurs. To avoid non-available user exception, I tried to move Load() and ExecuteQuery() to SetFilePermission method.
public async Task<IActionResult> AddPermission(Guid guid, String[] emailList)
{
using (var authenticationManager = new AuthenticationManager())
using (var context = authenticationManager.GetContext(_site, _user, _securePwd))
{
File file= context.Web.GetFileById(guid);
SetFilePermission(context, file, emailList);
// file.ListItemAllFields.SystemUpdate();
// context.Load(file.ListItemAllFields);
// await context.ExecuteQueryAsync();
return NoContent();
}
}
private static void SetFilePermission(ClientContext context, File file, string[] emailList)
{
if (emailList != null)
{
file.ListItemAllFields.BreakRoleInheritance(true, false);
var role = new RoleDefinitionBindingCollection(context);
role.Add(context.Web.RoleDefinitions.GetByType(permissionLevel));
foreach (string email in emailList)
{
User user = context.Web.SiteUsers.GetByEmail(email);
file.ListItemAllFields.RoleAssignments.Add(user, role);
// Move load and executequery method to here.
file.ListItemAllFields.SystemUpdate();
context.Load(file.ListItemAllFields);
context.ExecuteQueryAsync();
}
}
}
Suddenly, exception disappear even though the user is not available in SPO!
But other available emails in emailList also fail to add permission, just result in return NoContent eventually. Does anyone know the myth behind the process and explain it to me?

Calling Microsoft Graph API using first party app fails with insufficient privileges to complete the operation on "SubscribedSkus API"

Calling microsoft graph API https://graph.microsoft.com/v1.0/subscribedSkus fails with
"code": "Authorization_RequestDenied",
"message": "Insufficient privileges to complete the operation.",
This is happening if we create a new user in the tenant who is non admin. But while calling this with Admin user it works just fine. Even it works for any microsoft user in the tenant.
This is the below code I used to try.
public static async Task TestAadGraph()
{
// using obo token of the user.
var graphToken = await GetTokenAsync(UserId, Token, "https://graph.microsoft.com");
var aadGraphClient = new AadGraphClient(new HttpClient());
var licenseResponse = await aadGraphClient.GetTenantLicenseDetailAsync(graphToken);
foreach (var license in licenseResponse)
{
Console.WriteLine("Sku ID: {0}", license.SkuId);
Console.WriteLine("Sku Part Number: {0}", license.SkuPartNumber);
foreach (var plan in license.ServicePlans)
{
Console.WriteLine("Plan Id: {0}", plan.ServicePlanId);
Console.WriteLine("Plan Name: {0}", plan.ServicePlanName);
}
}
}
public async Task<SubscribedSku[]> GetTenantLicenseDetailAsync(string accessToken)
{
var request = new RequestMessage
{
BearerToken = accessToken,
Endpoint = new Uri("http://graph.microsoft.com/v1.0/subscribedSkus"),
};
var response = await _httpClient.FetchAsync<SubscribedSkusResponse>(request);
return response.Value;
}
public static async Task<T> FetchAsync<T>(this HttpClient httpClient, RequestMessage request, Action<HttpResponseMessage, string> responseCallback) where T : class
{
request.Method = request.Method ?? HttpMethod.Get;
request.MediaType = request.MediaType ?? "application/json";
using (HttpRequestMessage message = new HttpRequestMessage(request.Method,
UrlHelper.AppendParameters(request.Params, request.Endpoint)))
{
if (!string.IsNullOrEmpty(request.BearerToken))
{
message.Headers.Authorization = new AuthenticationHeaderValue("Bearer",
request.BearerToken);
}
if (request.Headers != null)
{
foreach (KeyValuePair<string, string> header in request.Headers)
{
message.Headers.Add(header.Key, header.Value);
}
}
if (!string.IsNullOrEmpty(request.Content))
{
message.Content = new StringContent(request.Content, Encoding.UTF8,
request.MediaType);
}`
using (HttpResponseMessage response = await httpClient.SendAsync(message))
{
string json = await response.Content.ReadAsStringAsync();
if (responseCallback != null)
{
responseCallback?.Invoke(response, json);
}
if (response.IsSuccessStatusCode)
{
if (predictor != null)
{
json = predictor(JToken.Parse(json)).ToString();
}
return JsonConvert.DeserializeObject<T>(json);
}
else
{
throw new WebRequestException(response, json);
}
}
}
}
Firstly, try the same call for the new created user in Microsoft Graph Explorer to see if the same scene exists. If not, it means the there is nothing wrong with the new user.
Then debug your code and copy the graphToken into https://jwt.io/ and see if the Decoded result has one of the required Delegated permissions:Organization.Read.All, Directory.Read.All, Organization.ReadWrite.All, Directory.ReadWrite.All, Directory.AccessAsUser.All. Note that the "upn" should be the username of the new created user.
If the required permissions do not exist, you will need to assign permissions in the Azure AD app. See API permissions.
Perfect. Adding permission to the first party app has actually worked.

active-directory-xamarin-native-v2 sample project from Microsoft for Xamarin Forms is not working in iOS

I am working on office365 authentication , took the code from github, for android build it is working fine, but in iOS not getting data response (token) from office365.
project link - https://github.com/Azure-Samples/active-directory-xamarin-native-v2
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
async void OnSignInSignOut(object sender, EventArgs e)
{
AuthenticationResult authResult = null;
IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();
try
{
if (btnSignInSignOut.Text == "Sign in")
{
// let's see if we have a user in our belly already
try
{
IAccount firstAccount = accounts.FirstOrDefault();
authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
.ExecuteAsync();
await RefreshUserDataAsync(authResult.AccessToken).ConfigureAwait(false);
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign out"; });
}
catch (MsalUiRequiredException ex)
{
try
{
authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
.WithParentActivityOrWindow(App.ParentWindow)
.ExecuteAsync();
await RefreshUserDataAsync(authResult.AccessToken);
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign out"; });
}
catch(Exception ex2)
{
slUser.IsVisible = true;
}
}
}
else
{
while (accounts.Any())
{
await App.PCA.RemoveAsync(accounts.FirstOrDefault());
accounts = await App.PCA.GetAccountsAsync();
}
slUser.IsVisible = false;
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign in"; });
}
}
catch (Exception ex)
{
}
}
public async Task RefreshUserDataAsync(string token)
{
//get data from API
slUser.IsVisible = true;
HttpClient client = new HttpClient();
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me");
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
HttpResponseMessage response = await client.SendAsync(message);
string responseString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
JObject user = JObject.Parse(responseString);
slUser.IsVisible = true;
Device.BeginInvokeOnMainThread(() =>
{
lblDisplayName.Text = user["displayName"].ToString();
lblGivenName.Text = user["givenName"].ToString();
lblId.Text = user["id"].ToString();
lblSurname.Text = user["surname"].ToString();
lblUserPrincipalName.Text = user["userPrincipalName"].ToString();
// just in case
btnSignInSignOut.Text = "Sign out";
});
}
else
{
await DisplayAlert("Something went wrong with the API call", responseString, "Dismiss");
}
}
}
}
I should get the same as android build, but in iOS not getting token from office365 server, seems like certificate issue, Below lines I found which are need to follow, in order to work in iOS
Also, in order to make the token cache work and have the AcquireTokenSilentAsync work multiple steps must be followed :
1)Enable Keychain access in your Entitlements.plist file and specify in the Keychain Groups your bundle identifier.
2)In your project options, on iOS Bundle Signing view, select your Entitlements.plist file for the Custom Entitlements field.
3) When signing a certificate, make sure XCode uses the same Apple Id.
Above are from Microsoft website
below is Entitlements.plist file details -
keychain-access-groups I am using $(AppIdentifierPrefix)com.oauth.office365 where com.oauth.office365 is my bundle identifier

WepApi TaskCanceledException A task was canceled. httpClient

It was working good, but I made some changes in the api, adding more controllers nothing out of the place, and then it stops working, always thrown an exception: "TaskCanceledException: A task was canceled" in the line GetAsync().result. I increase the timeout and infinitely stays loading.
The code controller APP who make a request to the controller API:
public ActionResult Login(LoginM us)
{
try
{
cuentaM account = new cuentaM();
HttpClient client = new HttpClient();
var result = client.GetAsync("http://localhost:26723/api/Login" + "?email=" + us.email + "&password=" + us.password).Result;
if (result.IsSuccessStatusCode)
{
account = result.Content.ReadAsAsync<cuentaM>().Result;
}
Session["cuenta"] = account;
return RedirectToAction("Index", "Home");
}
catch (Exception ex)
{
throw;
}
}
The controller API code:
public HttpResponseMessage Get(string email, string password)
{
try
{
using (elevationbEntities db = new elevationbEntities())
{
usuario user = db.usuarios.Where(m => m.email == email && m.password == password).SingleOrDefault();
cuentaM account = new cuentaM();
if (user != null)
{
account = (from o in db.cuentas
join cu in db.cuentausuarios on o.idCuenta equals cu.idCuenta
join u in db.usuarios on cu.idUsuario equals u.idUsuario
where u.idUsuario == user.idUsuario
select new cuentaM { idUsuario = user.idUsuario, idCuenta = o.idCuenta, CodigoUnico = o.CodigoUnico })
.FirstOrDefault();
}
else
{
account.Error = "Wrong Password or Email";
}
HttpResponseMessage response;
response = Request.CreateResponse(HttpStatusCode.OK, account);
return response;
}
}
catch (TaskCanceledException ex)
{
HttpResponseMessage response;
response = Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
return response;
}
}
Making blocking calls (.Result) on HttpClinet's async API can cause deadlocks especially if being used asp.net MVC, which may have async operations being invoked when the blocking call was made.
Make the code async all the way through.
Also try to avoid creating an instance of HttpClient on every request. This can cause sockets to be exhausted.
private static HttpClient client = new HttpClient();
public async Task<ActionResult> Login(LoginM us) {
try {
cuentaM account = new cuentaM();
var url = "http://localhost:26723/api/Login" + "?email=" + us.email + "&password=" + us.password
var result = await client.GetAsync(url);
if (result.IsSuccessStatusCode) {
account = await result.Content.ReadAsAsync<cuentaM>();
}
Session["cuenta"] = account;
return RedirectToAction("Index", "Home");
} catch (Exception ex) {
throw;
}
}
You may be deadlocking by blocking on an async call, as described in this article. Here's the problematic line:
account = result.Content.ReadAsAsync<cuentaM>().Result;
Change the method signature for Login to:
public async Task<ActionResult> Login(LoginM us)
Then change the problematic line to use await instead of .Result:
account = await result.Content.ReadAsAsync<cuentaM>();

Upload video on YouTube channel without end users login prompt using Google.Apis.YouTube.v3

I want use Google.Apis.YouTube.v3 for video upload on YouTube.
My requirement is Upload video on YouTube channel without end users login prompt.
So please let me know where there is some method where i can validate the user at server side and upload video directly to my channel in YouTube.
I want to implement in MVC C# application
[HttpPost]
public async Task<ActionResult> UploadVideo(HttpPostedFileBase video, UploadVideoInfo info)
{
try
{
var httpPostedFile = Request.Files[0];
if (httpPostedFile != null)
{
UserCredential credential;
using (var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
//This OAuth 2.0 access scope allows an application to upload files to the
//authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
});
var videos = new Video();
videos.Snippet = new VideoSnippet();
videos.Snippet.Title = info.VideoTitle;
videos.Snippet.Description = info.VideoDescription;
videos.Snippet.Tags = new string[] { "tag1", "tag2" };
videos.Snippet.CategoryId = "22";
videos.Status = new VideoStatus();
videos.Status.PrivacyStatus = "public"; // or "public" or "unlisted"
var videosInsertRequest = youtubeService.Videos.Insert(videos, "snippet,status", video.InputStream, "video/*");
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
}
catch (Exception ex)
{
}
return View();
}
void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
Console.WriteLine("{0} bytes sent.", progress.BytesSent);
break;
case UploadStatus.Failed:
Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
break;
}
}
void videosInsertRequest_ResponseReceived(Video video)
{
Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
}

Resources