I want to get access token to use it to fetch email from outlook using microsoft graph api. My application will be Console based c# application which will run automatically after every 20 min and will fetch the email.
I am new to c# as well as microsoft graph, this is my first task related to these technology.
Problem is:
When i tried to fetch token using client_Credentials i was successfully able to do so, but now that token is expired i want to get new token and if I try to generate new token it is returning the expired one only.
Relevant code:
result = await context.AcquireTokenAsync(resourceUri, clientCredential);
Using AcquireTokenSilentAsync method return as error:
"Failed to acquire token silently as no token was found in the cache. Call method AcquireToken."
Relevant code:
result = await authContext.AcquireTokenSilentAsync(resourceUri, clientId);
My questions:
Is accessing token using client credential is correct way to fulfill my need?
I have read that using client_Credentials we do not need refresh_token, every time we try to connect we will get new token.
How to get new token every time I want to connect?
Any extra suggestion about how to approach to my main objective which are not asked in question would be dearly welcomed.
I'm attaching my code sample:
static async Task getAccessToken()
{
authContext = new AuthenticationContext("https://login.microsoftonline.com/<tenantId>");
try
{
result = await authContext.AcquireTokenSilentAsync(resourceUri, clientId);
}
catch (Exception ex)
{
Console.WriteLine(ex);
try
{
result = await authContext.AcquireTokenAsync(resourceUri, clientCredential);
Console.WriteLine("" + result.AccessToken+"\n\n");
}
catch (Exception e)
{
Console.WriteLine("\n AcquireTokenAsync failed\n");
Console.WriteLine(""+e);
}
}
if (result == null)
{
Console.WriteLine("Canceling attempt to get access token.\n");
return;
}
Console.WriteLine(result.AccessToken);
}
You're mixing a two different OAuth flows (Authorization Code and Client Credentials). You should only need to call AcquireTokenAsync with the correct credentials. Whenever you need a new token (each token lives about an hour), you re-execute this method to get a new token:
static async Task<AuthenticationResult> getAccessToken()
{
ClientCredential clientCredential = new ClientCredential("YOUR_APP_ID", "YOUR_APP_SECRET");
AuthenticationContext authContext = new AuthenticationContext("https://login.microsoftonline.com/YOUR_TENANT_ID");
AuthenticationResult result = null;
try
{
result = await authContext.AcquireTokenAsync("https://graph.microsoft.com", clientCredential);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
if (result == null)
Console.WriteLine("Canceling attempt to get access token.");
else
Console.WriteLine(result.AccessToken);
return result;
}
Related
I am using a windows application to upload and download a file from onedrive api.
Code to retrieve token (This code is directly downloaded from azure portal after creating an app registration)
string graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";
string[] scopes = new string[] { "user.read" };
AuthenticationResult authResult = null;
var app = App.PublicClientApp;
ResultText.Text = string.Empty;
TokenInfoText.Text = string.Empty;
var accounts = await app.GetAccountsAsync();
var firstAccount = accounts.FirstOrDefault();
try
{
authResult = await app.AcquireTokenSilent(scopes, firstAccount)
.ExecuteAsync();
}
catch (MsalUiRequiredException ex)
{
System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
try
{
authResult = await app.AcquireTokenInteractive(scopes)
.WithAccount(firstAccount)
.WithParentActivityOrWindow(new WindowInteropHelper(this).Handle) // optional, used to center the browser on the window
.WithPrompt(Prompt.SelectAccount)
.ExecuteAsync();
}
catch (MsalException msalex)
{
ResultText.Text = $"Error Acquiring Token:{System.Environment.NewLine}{msalex}";
}
}
This is the code to get the download url of an item from onedrive api
string url="https://graph.microsoft.com/v1.0/me/drive/root:/Qwerty/test.txt";
string token=authResult.AccessToken;
var httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response;
try
{
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
//Add the token in Authorization header
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
Response cls = new Response();
cls.Success = "TRUE";
cls.Method = "GetAllFiles";
cls.Data = content;
return cls;
}
else
{
Response cls = new Response();
cls.Success = "FALSE";
cls.Method = "GetAllFiles";
cls.Data = content;
return cls;
}
Im getting this error "Must be authenticated to use '/drive' syntax". this app works with one of my personal app registration . but when i use the below app registration its strating to show this error. i followed the exact same steps in creating the app registration i dnt knw why this error.
client id with error: 463921cd-72a3-495d-847e-259b99dda89e
Please help me
This is the sreenshot
If you would like to download the contents of a DriveItem, you must add one of the following permissions to call this API. And you need to add the delegated permission when using /me. The scope in your code is also changed with permission.
Then you could refer to the code sample:
C# with graph SDK: https://stackoverflow.com/a/63806689/13308381
Try to follow these steps in Postman:
1.Request an authorization code
GET https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?client_id={client_id}&scope={scope}
&response_type=code&redirect_uri={redirect_uri}
2.Request an access token
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
client_id={client_id}&scope={scope}&redirect_uri={redirect_uri}&client_secret={client_secret}
&code={code}&grant_type=authorization_code
3.Call Microsoft Graph API
GET /me/drive/root:/{item-path}:/content
I have found out the issue. issue was when creating an access token we need specify scopes like(Files.Read,Files.ReadWrite,etc (add whatever we need)) and then use the same token for downloading's a file and it was ok
I am moments from giving up entirely on this workflow.
I set up an Azure account with a REST API, a few other things, and then a b2c AD. After hours and hours of hating my life, I finally have it (somewhat) working to use this AD to sign in, and to be required to access the REST API.
The problem I am now having is that I have tried every single variation of anything I can find only about how to get the profile information of that person who is logged in, and I am getting absolutely nothing.
Even the example files after login don't properly work - it is meant to redirect you to a page that says welcome (username) but the username does not return in my result to
result = await App.AuthenticationClient
.AcquireTokenInteractive(B2CConstants.Scopes)
.WithPrompt(Microsoft.Identity.Client.Prompt.SelectAccount)
.WithParentActivityOrWindow(App.UIParent)
.ExecuteAsync();
None of this is the least bit intuitive. I have read docs but somehow every time I google it I end up on a different page of Microsoft docs with different information.
I do not know what I need to pass to scopes... I do not know how to property get an auth token, as every time I try and use graphs I get an error about an empty or invalid auth token (even after logging in).
Even though I logged in with google successfully, the result above gives me a NULL access token, null tenant id, null scopes, null username under account... the ONLY thing that is correct is the unique ID lines up with the ID I see on the AD users page.
EDIT: the AccessToken is not null actually, I get info there... but every call I try to make that I think should use it is failing.
Ie, if I try to call this to get my info after signing in:
InteractiveAuthenticationProvider authProvider = new InteractiveAuthenticationProvider(App.AuthenticationClient, B2CConstants.Scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var users = await graphClient.Me.Request().GetAsync();
Note the App.AuthenticationClient is what I used to login above - I have also tried this with
//.Create(B2CConstants.ClientId)
//.Build();
I get this (only partial error so I don't pass out IDs)
Microsoft.Graph.ServiceException: 'Code: InvalidAuthenticationToken
Message: Access token validation failure.
Inner error:
AdditionalData:
date: 2020-10-26T21:45:25
I suggest that you mostly refer Microsoft Docs for any information.
Consider below code to get users details from B2C (This is Android Xamarin Forms page .cs code):
async void OnSignInSignOut(object sender, EventArgs e)
{
AuthenticationResult authResult = null;
IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();
try
{
if (btnSignInSignOut.Text == "Sign in")
{
try
{
IAccount firstAccount = accounts.FirstOrDefault();
authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
.ExecuteAsync();
}
catch (MsalUiRequiredException ex)
{
try
{
authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
.WithParentActivityOrWindow(App.ParentWindow)
.ExecuteAsync();
}
catch(Exception ex2)
{
await DisplayAlert("Acquire token interactive failed. See exception message for details: ", ex2.Message, "Dismiss");
}
}
if (authResult != null)
{
var content = await GetHttpContentWithTokenAsync(authResult.AccessToken);
UpdateUserContent(content);
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign out"; });
}
}
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)
{
await DisplayAlert("Authentication failed. See exception message for details: ", ex.Message, "Dismiss");
}
}
For complete and step by step samples on B2C integration, you can visit below links:
Integrate Microsoft identity and the Microsoft Graph into a Xamarin forms app using MSAL
Integrate Azure AD B2C into a Xamarin forms app using MSAL
I am trying to call AcquireTokenAsync it is working properly but after sometime it is not responding and it is not providing any result.
please refer the below code how to solve my issue
public static async Task<string> GetToken(string authority, string resource, string scope)
{
var authContext = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(WebConfigurationManager.AppSettings["ClientId"],
WebConfigurationManager.AppSettings["ClientSecret"]);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCred);
if (result == null)
throw new InvalidOperationException("Failed to obtain the JWT token");
return result.AccessToken;
}
public static string GetKeyVaultSecret(string secretName)
{
try
{
var secretUri = WebConfigurationManager.AppSettings["SecretUri"];
var kv = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(GetToken));
var secret = kv.GetSecretAsync(secretUri, secretName).Result;
return secret.Value;
}
catch(Exception ex)
{
return null;
}
}
For access token, the default time is 1 hour. After 1 hour, the client must use the refresh token to (usually silently) acquire a new refresh token and access token.
You can change access token lifetime to the maximum to one day with this tutorial.
New-AzureADPolicy -Definition #('{"TokenLifetimePolicy":{"Version":1,"AccessTokenLifetime":"24:00:00","MaxAgeSessionSingleFactor":"02:00:00"}}') -DisplayName "WebPolicyScenario" -IsOrganizationDefault $false -Type "TokenLifetimePolicy"
For more details about token lifetime you can refer to this article.
I've created a Bot application in Visual Studio 2017, which I want to use in MS Teams. This application is part of a solution, which contains 2 components, the bot application itself and a windows application, which I have created that is used by the bot application to retrieve an authentication token from Microsoft (using similar code to what is on this website https://learn.microsoft.com/en-us/azure/active-directory/develop/guidedsetups/active-directory-uwp-v2).
When debugging the bot after hosting it locally, I'am able to use the bot successfully in Teams. There is no error. However, now that I have registered the bot with the Microsoft Bot Framework in Azure, I'm now having issues as Teams returns back the message "Sorry, my bot code is having an issue." In Azure I have a Bots Channels Registration entity, which in its settings points to a messaging endpoint that is https://.azurewebsites.net/api/messages. I also have a Apps Service. Now I have transferred the application id that I received when registering the bot with the Microsoft Bot Framework and have put this into the bot application in Visual Studio in the web.config file along with the app password.
After testing this in the Bot Framework Emulator I get "POST 401 directline.postActivity" and in the "Inspector-JSON" I get "BotAuthenticator failed to authenticate incoming request!". This is my first bot application so I'm lost as to what I have potentially missed out so does anyone have any idea what I could try?
So here's what I have in my RootDialog.cs file, which where the endpoint will hit when the bot is used.
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
string userInfo = "";
AuthTokenDeploy tokenDeploy = new AuthTokenDeploy();
userInfo = await tokenDeploy.MsGraphUserInfo();
if(!userInfo.Equals(""))
{
// send webhook to end user system
await SendToEndpoint(context, activity, activity.Text,
userInfo);
}
}
AuthTokenDeploy is an instance of another class, which is where the function to obtain the access token along with the user information from Microsoft is held. So I created a string "userInfo", which then takes the value given by MsGraphUserInfo().
public async Task<string> MsGraphUserInfo()
{
AuthenticationResult authResult = null;
string Text = null;
try
{
authResult = await App.PublicClientApp.AcquireTokenSilentAsync(_scopes, App.PublicClientApp.Users.FirstOrDefault());
}
catch (MsalUiRequiredException ex)
{
// A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token
System.Diagnostics.Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
try
{
authResult = await App.PublicClientApp.AcquireTokenAsync(_scopes);
}
catch (MsalException msalex)
{
}
}
catch (Exception ex)
{
}
if (authResult != null)
{
Text = await GetHttpContentWithToken(_graphAPIEndpoint, authResult.AccessToken);
}
return Text;
}
Calling MsGraphUserInfo() will open the "AuthToken.exe" as a popup Window and ask the user to log in with their credentials. You can see from the code above that it acquires the access token first, which is then passed into GetHttpContentWithToken(), which is where a HTTP GET request is run against "https://graph.microsoft.com/v1.0/me" and a JSON string is returned with the user information in it.
public async Task<string> GetHttpContentWithToken(string url, string token)
{
var httpClient = new System.Net.Http.HttpClient();
System.Net.Http.HttpResponseMessage response;
try
{
var request = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
//Add the token in Authorization header
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
return content;
}
catch (Exception ex)
{
return ex.ToString();
}
}
Now I feel as if this method of using a Windows application to allow the user to log in might not be the best way forward, hence why I've been reading and following this guide https://learn.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-auth-aad. I would like to know whether it is possible to use what is on this page to allow my bot to retrieve an access token?
I am working on Windows Broker Authentication.I can successfully authenticate in to the calling app and can comeback to the home page after authentication.
I am not able to get the user info (username) .I have tried but I get one message as written below.
A first chance exception of type 'System.Exception' occurred in mscorlib.dll
WinRT information: Response status code does not indicate success: 401 (Unauthorized).
Additional information: Unauthorized (401).
Response status code does not indicate success: 401 (Unauthorized).
If there is a handler for this exception, the program may be safely continued.
I have written my code below.Please friends help me.
private const string RESOURCE_NAME ="id_token";
public async Task<UserInfo> GetName(string accessToken)
{
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new Windows.Web.Http.Headers.HttpCredentialsHeaderValue("OAuth", accessToken);
var result = await client.GetStringAsync(new Uri(loginUri));
var profileInformation =JsonObject.Parse(result).GetObject();
var name = profileInformation.GetNamedString("username");
return new UserInfo { Name = name };
}
catch (JsonException ex)
{
throw new JsonException(ex.message);
}
}
private async void btnHomeLogin_Click(object sender, RoutedEventArgs e)
{
string Scope = "openid profile";
var client = new OAuth2Client(new Uri(loginUri));
var startUri = client.CreateAuthorizeUrl(
ClientID,
RESOURCE_NAME,
Scope,
RedirectURI,
state,
nonce);
string Authresult;
try
{
var webAuthenticationResult = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, new Uri(startUri),new Uri(RedirectURI));
switch (webAuthenticationResult.ResponseStatus)
{
case Windows.Security.Authentication.Web.WebAuthenticationStatus.Success:
//Successful authentication.
Authresult = webAuthenticationResult.ResponseData.ToString();
UserInfo userInfo = await GetName(RESOURCE_NAME);
break;
case Windows.Security.Authentication.Web.WebAuthenticationStatus.ErrorHttp:
//HTTP error.
Authresult = webAuthenticationResult.ResponseErrorDetail.ToString();
break;
default:
//Other error.
Authresult = webAuthenticationResult.ResponseData.ToString();
break;
}
}
catch (Exception ex)
{
//Authentication failed. Handle parameter, SSL/TLS, and Network Unavailable errors here.
Authresult = ex.Message;
}
}
If you are exactly using the above code, then based on the above, you are calling GetName function with a constant string (RESOURCE_NAME) instead of the actual AuthResult (accessToken) that was returned from the webAuthenticationResult. If the intention of calling the WebAuthenticationBroker is to get back an Access Token which should later be used with HttpClient, then you need to adjust your code accordingly and make use of the right access Token when calling the HttpClient code. Otherwise the 401 is not unexpected if you are not passing the right token.