I want to programmatically get access token for the current user after logging in. I've figured out how to get a token using client credentials but I couldn't figure out how to get one on behalf of the user.
Here's what I tried to get using client credentials:
var client = new TokenClient("http://localhost:34240/connect/token", "client", "secret", AuthenticationStyle.PostValues);
var token = client.RequestClientCredentialsAsync(scope: "api").GetAwaiter().GetResult();
Do I need to use acr_values to add subject value to the request? If yes, how do I add it to the returned access token?
Or do I need to use code grant type instead? If yes, how do I request an authorization code programmatically?
Or is there another way that I'm missing?
I'd appreciate any help. I've checked IdentityServer samples but couldn't see anything about this.
Have a look at the Resource owner password grant examples. Basically you are doing almost the same, like you are currently doing, but instead of client credentials grant, you need to setup your client to use ResourceOwnerPassword, and then the code that you've shown, changes to:
var client = new TokenClient("http://localhost:34240/connect/token", "client", "secret", AuthenticationStyle.PostValues);
var token = client.RequestResourceOwnerPasswordAsync("<username>", "<password>", scope: "api").GetAwaiter().GetResult();
By this you are getting a token on behalf of the user. But have in mind:
The spec recommends using the resource owner password grant only for “trusted” (or legacy) applications. Generally speaking you are typically far better off using one of the interactive OpenID Connect flows when you want to authenticate a user and request access tokens.
Related
I am getting the following error when trying to access the directory api by getting a list of users
Google.Apis.Auth.OAuth2.Responses.TokenResponseException: Error:"unauthorized_client", Description:"Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.", Uri:""
Based on my previous googling efforts and reading stack overflow I am not sure what is causing this. As far as I am aware I have everything set up correctly. I have it setup to use the drive api in a very similar fashion and that works perfectly.
My service account does have domain wide delegation which is why I think it may have something to do with the second part of the error. Any ideas what could be causing this?
protected async virtual Task<DirectoryService?> GetDirectoryService()
{
if (currentDirectory == null)
{
string[] scopes = new string[] { DirectoryService.Scope.AdminDirectoryUser };
var initializer = new ServiceAccountCredential.Initializer(configuration["GoogleServiceAccount:AccountEmail"]){Scopes = scopes, User = configuration["GoogleServiceAccount:UserEmail"] };
var cred = new ServiceAccountCredential(initializer.FromPrivateKey(configuration["GoogleServiceAccount:SecretKey"]));
currentDirectory = new DirectoryService(new BaseClientService.Initializer { HttpClientInitializer = cred, ApplicationName = "DriveAPI" });
}
return currentDirectory;
User = configuration["GoogleServiceAccount:UserEmail"]
User is the user on your domain that you want to delegate as not the service accounts email address.
update
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested
This error message in my exprence normally means that you are using code that does not match the type of credetinals you are using.
There are serval types of authorization, service account, Oauth installed and oauth web (lets ignore mobile for now). The code used for these credentials is different.
So if you use a service account key file with code designed for an Ouath2 installed app. You will normally get "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested"
The code I normally use for delegation with a service account is as follows. it looks similar to yours so i'm inclined to agree that you are using service account code. Which means to me you are probably using the wrong key file. I would double check. Open it it should say "type": "service_account".
// Load the Service account credentials and define the scope of its access.
var credential = GoogleCredential.FromFile(PathToServiceAccountKeyFile)
.CreateWithUser("user#mydomain.com")
.CreateScoped(new[] {DriveService.ScopeConstants.Drive});
My suggestion is to now double check and ensure that you are using the service account key file from google cloud console that was created by a user on your domain, and that you configured domain wide deligation for and have added the admin sdk scopes for remember the OAuth Scopes for admin have to be set, as well configuring an authorized user.
I'm trying to connect to Graph API and get user access token.
My problem is that I don't know how to connect to Graph API with credentials silently (without browser).
I currently use MSLogin() for get access token but it open a browser where you can authorize an AzureAD app to get some access to your account. A library in Java is litteraly what I want in c# https://github.com/Litarvan/OpenAuth
I need something like: MSGraph.ConnectAsync(email, pass).getAccessToken();
Here my current code (Through a browser)
private const string ClientId = "520f6e8e-xxxxxxxxxxxxxxxxxxxx";
private string[] scopes = { "https://graph.microsoft.com/user.read" };
private static AuthenticationResult authResult;
public static IPublicClientApplication PublicClientApp;
private async Task<AuthenticationResult> MSLogin()
{
PublicClientApp = PublicClientApplicationBuilder.Create(ClientId).WithRedirectUri("msal520f6e8e-xxxxxxxxxxxxxxxxxxxxxxxxxxxxx://auth").Build();
authResult = await PublicClientApp.AcquireTokenInteractive(scopes).ExecuteAsync();
return authResult;
}
If you are using Microsoft Graph .NET Client Library you can check documentation with example how to implement username/password authentication flow.
string[] scopes = {"User.Read"};
var usernamePasswordCredential = new UsernamePasswordCredential("username#domain.com", "password", tenantId, clientId);
var graphClient = new GraphServiceClient(usernamePasswordCredential, scopes);
var me = await graphClient.Me.Request().GetAsync();
You can use AcquireTokenByUsernamePassword() for that, see MSDN.
Note however that Microsoft discourages usage of this flow and depending on your AzureAD setup there might be restrictions (i.e. you can aquire tokens only within a certain IP range etc).
Well, you can get the access token silently but not at the first time, First a user must authorize your app by going through Microsoft's Login flow and for your subsequent calls to Microsoft, you can get the access token without the intervention of user.
I would just give a basic idea, without focusing on a specific SDK that you might be using. For which, you can decide which ever method suits your needs.
I assume, you already have your credentials and desired scopes with you, otherwise you need to obtain those.
Formulate a proper URL using the credentials you obtained, plus you need to add an extra scope in the URL which is offline_access. Then you need to redirect the user to Microsoft for the initial authorization.
If the user logs in successfully, Microsoft will redirect the user back to your website with an Authorization Code.
Grab that Authorization Code and exchange it for an Access Token using /oauth2/{version}/token api.
You will receive a response from above call which will contain an Access Token along with a Refresh Token. You need to store the refresh token somewhere for future use.
Now comes the interesting part.
Using the refresh token, you can renew the access token when it expires without user's intervention. You can use oauth2/v2.0/token api with parameters:
client_id={your_client_id}&scope={your_scopes}&refresh_token={refresh_token_obtained}&grant_type=refresh_token&client_secret={your_client_secret}
The resultant response would look something like this:
{
"access_token": "new access token",
"token_type": "Bearer",
"expires_in": 3599,
"scope": "your scopes",
"refresh_token": "refresh token",
}
REF: https://learn.microsoft.com/en-us/graph/auth-v2-user#authorization-request
I have created a special service account in AAD that I want to use to send email notifications to users.
In asp.net core 2 web app, how do I get access token for that service account?
The samples I've seen uses user's identity, but that is not my case.
I will have probably some background process, so there cannot be any user interactivity.
I will have probably some background process, so there cannot be any user interactivity.
you could use OAuth 2 Resource Owner Password Credentials grant. Note: The resource owner password grant doesn't provide consent and doesn't support MFA either. Detailed tutorial, you could follow here. Moreover, you could use ADAL to retrieve the access_token instead of constructing the HttpClient by yourself.
The code for acquire the token via ADAL would look like:
var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com","<clientId>", new UserPasswordCredential("<userName>", "<password>"));
Moreover, as juunas commented that you could use the service to service scenario and use the application permissions instead of the user delegated permissions. The code would look like this:
var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com", new ClientCredential("<clientId>", "<clientSecrets>"));
For your AAD app on Azure Portal, you need to configure the required permissions for accessing Microsoft Graph API as follows:
Note: For Send mail Microsoft graph API, you need to set the related permission. For Resource Owner Password Credentials grant flow, you need to choose the delegated permission Send mail as a user (Mail.Send). While for client credential flow, you need to choose the application permission Send mail as any user (Mail.Send), then click grant permissions (this permission needs global admin to consent).
After retrieved the access_token, you could use Microsoft Graph Client Library for .NET to communicate with Microsoft Graph API to send the email. The initialization for GraphServiceClient would look like this:
//initialize the GraphServiceClient instance
var graphClient = new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
var token = await GetAccessTokenAsync();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}));
I am creating a console application that connects to Microsoft Graph using the Microsoft Graph API (as shown in https://github.com/microsoftgraph/console-csharp-connect-sample).
Everything is working fine, but I wonder if there is a way where I can authenticate a user (when I already know their user/password) without them needing to manually enter their credentials on the "Sing in to your account" window rendered on the desktop.
The idea is basically to run the application unattended, so there is no need for the user to be entering their credentials when the application starts. I can´t find any relevant information on the subject.
Is that even possible?
EDIT
After following the link #DanSilver posted about geting access without a user, I tried the sample suggested in that link (https://github.com/Azure-Samples/active-directory-dotnet-daemon-v2). Although that is an MVC application that forces users to authenticate (precisely what I wanted to avoid) I have managed to use part of the authentication code in that sample with my console application. After giving authorization to the application manually through a request to https://login.microsoftonline.com/myTenantId/adminconsent I can create a GraphServiceClient in my console app that connects to Graph without user interaction. So I mark the answer as valid.
Just in case someone is in the same situation, the GraphServiceclient is created as:
GraphServiceClient graphServiceClientApplication = new GraphServiceClient("https://graph.microsoft.com/v1.0", new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string clientId = "yourClientApplicationId";
string authorityFormat = "https://login.microsoftonline.com/{0}/v2.0";
string tenantId = "yourTenantId";
string msGraphScope = "https://graph.microsoft.com/.default";
string redirectUri = "msalXXXXXX://auth"; // Custom Redirect URI asigned in the Application Registration Portal in the native Application Platform
string clientSecret = "passwordGenerated";
ConfidentialClientApplication daemonClient = new ConfidentialClientApplication(clientId, String.Format(authorityFormat, tenantId), redirectUri, new ClientCredential(clientSecret), null, new TokenCache());
AuthenticationResult authResult = await daemonClient.AcquireTokenForClientAsync(new string[] { msGraphScope });
string token = authResult.AccessToken;
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
));
One idea is using the "app only" authorization flow. The idea is that you can have long running apps access the Microsoft Graph without user authentication. The main difference is instead of the access token granting access to a particular user, it grants your app access to resources that you've consented to in advance. There will be no user login dialog and you can programmatically fetch access tokens to call the Graph API.
To reiterate that these tokens aren't for a particular user, consider making a GET request to 'https://graph.microsoft.com/v1.0/me'. This will return an error since the access token isn't for a particular user and "me" doesn't mean anything. Requests should be sent with full user ids "like graph.microsoft.com/users/someuser#contosos.com".
More information on this can be found at the Get access without a user documentation page.
Another idea is to let the user authenticate the first time they use your app and then store a refresh token. These tokens live longer (a few months IIRC) and then you won't need to prompt for user consent each time the app runs. Refresh tokens can be exchanged for access tokens that live 60 minutes and those can be used to call Graph API on behalf of users.
More info on refresh tokens: https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_user#5-use-the-refresh-token-to-get-a-new-access-token
I did want to come back out here and share, since I ran into this problem yesterday, and the idea of granting read/write mailbox access for my application... to EVERYONE'S EMAIL BOX IN THE ENTIRE ORGANIZATION... was way over the top for my needs. (And that is exactly what happens when you start talking about granting Application level permissions instead of delegated permissions to your registered app).
It's a simple use case: I had a nightly process that needed to automate sending of emails from a shared mailbox using a traditional AD service account.
Thankfully... even though they are on the march to eliminate passwords (lol)... someone at Microsoft still recognizes my use case, and it's lack of apples-to-apples alternatives in Azure AD. There is still an extension method we can lean on to get the job done:
private AuthenticationContext authContext = null;
authContext = new AuthenticationContext("https://login.microsoftonline.com/ourmail.onmicrosoft.com",
new TokenCache());
result = authContext.AcquireTokenAsync("https://graph.microsoft.com/",
"12345678-1234-1234-1234-1234567890",
new UserPasswordCredential(
Environment.GetEnvironmentVariable("UID", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("UPD", EnvironmentVariableTarget.User)
)).Result;
You can replace those GetEnvironmentVariable calls with your Username (UID) and Password (UPD). I just stuff them in the environment variables of the service account so I didn't have to check anything into source control.
AcquireTokenAsync is an extension method made available from the Microsoft.IdentityModel.Clients.ActiveDirectory namespace. From there, it's a simple business to fire up a GraphClient.
string sToken = result.AccessToken;
Microsoft.Graph.GraphServiceClient oGraphClient = new GraphServiceClient(
new DelegateAuthenticationProvider((requestMessage) => {
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("bearer", sToken);
return Task.FromResult(0);
}));
The last bit of magic was to add these permissions to Application registration I created in Azure AD (where that GUID came from). The application has be defined as a Public client (there's a radio button for that towards the bottom of the authentication tab). I added the following 5 DELEGATED permissions (NOT application permissions):
Microsoft Graph
1. Mail.ReadWrite.Shared
2. Mail.Send.Shared
3. User.Read
4. email
5. openid
Since user consents are actually blocked in our organization, another permissions admin had to review my application definition and then do an admin level grant of those rights, but once he did, everything lit up and worked like I needed: limited access by a service account to a single shared mailbox, with the actual security of that access being managed in Office 365 and not Azure AD.
I am exploring Azure Active Directory. I am trying to see whether I can use my own login page with custom user id/password controls to capture the user credentials and validate against Azure AD. I am using ADAL.net to implement this, however I get an error "parsing_wstrust_response_failed: Parsing WS-Trust response failed". I get this error on the last line of the below code.
The below is my code:
string AppIdURL = ConfigurationManager.AppSettings["AppIdUrl"];
UserCredential uc = new UserPasswordCredential("testuser#domain.com", "test123");
AuthenticationContext aContext = new AuthenticationContext(System.Configuration.ConfigurationManager.AppSettings["AADInstance"]);
AuthenticationResult result = aContext.AcquireTokenAsync(AppIdURL, ConfigurationManager.AppSettings["ClientId"], uc).Result;
Please first click here to view the constraints and limitations of the Resource Owner Password Credentials Grant flow . Base on your error message , is the user federated with WS-Trust ? Please provide more information about your current configuration to help us reproduce this error .
In fact, Resource Owner Password Credentials Grant flow is not recommend. This should only be used when there is a high degree of trust between the resource owner and the client (e.g., the client is part of the device operating system or a highly privileged application), and when other authorization grant types are not available (such as an authorization code).
If your aim is to customize the sign-in page ,such as add company branding to your sign-in page , you could click here for how to customize the sign-in page .