OpenId IdentityServer AutoLogin - identityserver4

I wanted to see if there is a way to Autologin a user where they have a ClientID and ClientSecret. I can get a TokenClient but once I get that I am not sure what I can do in order to AutoLogin a user.
var client = new TokenClient(string.Concat(this.BaseAddress, IdentityServerTokenServiceUrl), ClientId, ClientSecret, AuthenticationStyle.PostValues);

Related

How can i get a token expiry when i authenticate to Salesforce using OAuth2?

I have set up a connected app with the following OAuth scopes
Access the identity URL service (id, profile, email, address, phone)
Manage user data via APIs (api)
Manage user data via Web browsers (web)
Perform requests at any time (refresh_token, offline_access)
Access custom permissions (custom_permissions)
I first authenticate using the following
$"{this.ServiceUrl}/authorize?response_type=code&client_id={this.ClientId}&redirect_uri=<HOST_NAME>/SalesForce/MySFCallback";
this presents me with the Salesforce login screen and once i have successfully logged in , I am returned back to my web page and then i try to
get a token as follows:
Using the code value returned from the callback , I call
var client = new RestClient(Uri + "/token");
var request = new RestRequest("", Method.POST);
request.AddParameter("grant_type", "authorization_code", ParameterType.GetOrPost);
request.AddParameter("code", code, ParameterType.GetOrPost);
request.AddParameter("client_id", clientId, ParameterType.GetOrPost);
request.AddParameter("client_secret", clientsecret, ParameterType.GetOrPost);
request.AddParameter("redirect_uri", $"{callbackUri}", ParameterType.GetOrPost);
var response = client.Execute(request);
I get back the JSON response , but there is no expiry for the token.
How can i get an expiry for the token ?
Have you seen https://salesforce.stackexchange.com/q/73512/799 ? Very good answers there.
In a pinch your administrator could try creating a custom number field on User based on user's profile and then expose it to you as a custom attribute (you'd see the extra field in the base64-encoded id_token piece, if you requested this response_type).
But really admin can:
terminate sessions at any time (Setup -> Session Management),
revoke your connected app's access
other things can kick in like you switching IPs (from office vpn to home network or cellular data plan) and company having Setup -> Session Settings -> Lock sessions to IP from which they originated...
So at best this thing would be indicative timeout.
Try to code it defensively based on refresh tokens like in sfdcfox's answer

Azure AD Bearer Token has wrong "aud" claims

I am trying to use AAD delegated permission Bearer tokens for a Visio VSTO addin to create SharePoint Online pages using CSOM. Initially I was able to get this working entering username / password following Modern Authentication with CSOM for Net Standard
However, I would like for the user to select an existing AAD account. When I attempt to use the following code the Bearer token "aud" claim is consistently set to "00000003-0000-0000-c000-000000000000" which is the Graph API. Whilst a ClientContext object is returned I am getting a HTTP 401 Unauthorized error when performing a page lookup.
The code is as follows
//
// Get Client App
//
var ClientApp = (PublicClientApplication)PublicClientApplicationBuilder.Create(<AAD App ID>)
.WithDefaultRedirectUri()
.WithTenantId(<AAD Tenant ID>)
.WithAuthority(AzureCloudInstance.AzurePublic, <AAD Tenant ID>)
.Build();
//
// Prompt for user to select preferred AAD account
// The returned JWT Bearer Token "aud" claim is 00000003-0000-0000-c000-000000000000
//
var Token = ClientApp.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(GetActiveWindow())
.ExecuteAsync()
.GetAwaiter()
.GetResult();
//
// Get client Context
//
var ClientContext = AuthenticationManager.GetAzureADAccessTokenAuthenticatedContext(<SharePoint Site URL>, Token.AccessToken);
//
// Using the Client Context to query the Site results in HTTP 401
//
ClientContext.Load(ClientContext.Web, p => p.Title, t => t.Description);
ClientContext.ExecuteQuery();
Looking at the code for the AuthenticationManager class in the above link I can see that the AAD Bearer request is passing the following resource request parameter to the SharePoint online URL:
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={HttpUtility.UrlEncode(username)}&password={HttpUtility.UrlEncode(password)}";
So it seems that AAD is setting the Bearer token "aud" claim based upon this parameter. However, when I try and add this parameter using 'WithExtraQueryParameters' I am getting the following error: "AADSTS901002: The 'resource' request parameter is not supported"
Ok, I figured out the problem. The scope needs to be prefixed with the resource:
string[] scopes = { "https://<domain>.sharepoint.com/AllSites.Write", "user.read" }
Then retrieve the token
this.Token = await ClientApp.AcquireTokenInteractive(scopes)
.WithPrompt(Prompt.SelectAccount)
.WithParentActivityOrWindow(GetActiveWindow())
.ExecuteAsync();

Microsoft graph API to send Email using token authentication using client secret . I have pasted token in https://jwt.ms/ but missing scope

I am using Graph API to send emails and using clientID, Secret and APPID. I'm getting the token, but I'm unable to send emails:
Code: NoPermissionsInAccessToken Message: The token contains no
permissions, or permissions can not be understood. Inner error:
AdditionalData: requestId: 53f1cddb-4f38-4efa-ab62-624b495374f0 date:
2020-12-02T10:46:42 ClientRequestId:
53f1cddb-4f38-4efa-ab62-624b495374f0
I have added API permission as delegated to mail.send
IPublicClientApplication publicclientapplication = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.Build();
UsernamePasswordProvider authprovider = new UsernamePasswordProvider(publicclientapplication,scopes);
var authResult = await publicclientapplication
.AcquireTokenByUsernamePassword(scopes, username, passwordstring(stringpassword))
.ExecuteAsync().ConfigureAwait(false);
return authResult.AccessToken;
Code that I'm using to send is:
await graphServiceClient.Me .SendMail(email, false) .Request() .PostAsync();
Please note that delegated permission is for app+user authentication while application permission is for app-only authentication.
You should have used a wrong Microsoft Graph authentication provider or you have added a wrong type of permission.
Now you should be using Client credentials provider, which uses client credential flow and application permission. But you didn't add application permission in your Azure AD app. It's why you get the error The token contains no permissions. If your application doesn't require a user to sign in, you could use this client credentials provider. But you need to add application permission instead of delegated permission into AAD app and remember to use graphServiceClient.Users["{id or userPrincipalName}"].SendMail to send the mail.
For delegated permission and graphServiceClient.Me endpoint, you should choose Authorization code provider which requires you to implement sign-in interactively. Keep using delegated permission and graphServiceClient.Me.SendMail.
If you don't want to sign in interactively but want to have a user in the access token, you need to choose Username/password provider. But it's not recommended by Microsoft. See Warning here. Keep using delegated permission and graphServiceClient.Me.SendMail as well.
UPDATE:
A sample with Username/password provider:
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.Build();
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var email = "Your Username Here";
var str = "Your Password Here";
var password = new SecureString();
foreach (char c in str) password.AppendChar(c);
//prepare message and saveToSentItems here
await graphClient.Me
.SendMail(message,saveToSentItems)
.Request()
.PostAsync();
UPDATE 2:
Add application permission Mail.Send into Azure AD app.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
//prepare message and saveToSentItems here
await graphClient.Users["{id or userPrincipalName}"]
.SendMail(message,saveToSentItems)
.Request()
.PostAsync();

IdentityServer4 - Is there a way to silently authenticate user as part of the authorization code flow?

My scenario is that there is only one user for a client and that user will be used to fetch/create the API data. I want to use authorization code flow (or whichever suits?) and silently authenticate that one user and generate id_token and access token. My client don't want the login screen to authenticate (perhaps they already have user authenticated in their app) for now as there is only going to be one user.
How will this be achieved and using what grant_type ? Or it there a way to Auto login user using their username and password?
If you have their username and password you can use the Token Endpoint:
POST /connect/token
client_id=yourclientid& client_secret=yourclientsecret&
grant_type=password&
username=yourusername&password=yourusernamespassword
You can use Identity Model to help you make the token request:
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = "https://demo.identityserver.io/connect/token",
ClientId = "yourclientid",
ClientSecret = "yourclientsecret",
UserName = "yourusername",
Password = "yourusernamespassword"
});

AADSTS50013: Assertion audience claim does not match the required value

I've got a single page app that authenticates users in Azure using adal-angular.js/adal.js [client].
The returned token is inserted into the auth header and passed to a web API [server]. This web api generates a new access token for the app using the on-behalf-of workflow (https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof)
This token is then used to call a downstream API [API1].
So the downstream API then repeats this process to get a new token to call another API [API2]. It's at this point that I'm getting the above error.
The aud value in the token passed from [client] to [server] is the application id of the [server] app.
The aud value in the token passed from the [server] to [API1] is the Application URI of the [API1] app.
So far so good.
When I call AcquireTokenAsync in [API1] app, I get the following error:
AADSTS70002: Error validating credentials. AADSTS50013: Assertion audience claim does not match the required value. The audience in the assertion was 'http://application_uri.com/' and the expected audience is 'snip-a1d5-e82e84f4e19e' or one of the Application Uris of this application with App ID 'snip-a1d5-e82e84f4e19e'
The relevant code from [API1]:
public static async Task<string> GetApplicationTokenOnBehalfOfUser(string appId, string appKey)
{
var clientCredential = new ClientCredential(appId, appKey);
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as
System.IdentityModel.Tokens.BootstrapContext;
var userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
var userAccessToken = bootstrapContext.Token;
var userAssertion = new UserAssertion(userAccessToken, _assertionType, userName);
var authority = string.Format(System.Globalization.CultureInfo.InvariantCulture, _aadInstance, _tenant);
var userId = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var authContext = new AuthenticationContext(authority, new TokenCache());
var result = await authContext.AcquireTokenAsync(_resourceId, clientCredential, userAssertion);
var accessToken = result.AccessToken;
return accessToken;
}
Where:
appId = "snip-a1d5-e82e84f4e19e"
And the "aud" value from the BootstrapContext.Token is:
"aud": "http://application_uri.com/"
If I change the above to use the "aud" value from the token as the appId in the ClientCredential, I get this error instead:
AADSTS65001: The user or administrator has not consented to use the application with ID 'http://application_uri.com/'. Send an interactive authorization request for this user and resource.
Am I doing this right?
Thanks.
AADSTS70002: Error validating credentials. AADSTS50013: Assertion audience claim does not match the required value. The audience in the assertion was 'http://application_uri.com/' and the expected audience is 'snip-a1d5-e82e84f4e19e' or one of the Application Uris of this application with App ID 'snip-a1d5-e82e84f4e19e'
To use the on-behalf-of flow, we need to provide the access token for the API1 and provide the clientId and secrect of API1 to request the access token for the API2.
AADSTS65001: The user or administrator has not consented to use the application with ID 'http://application_uri.com/'. Send an interactive authorization request for this user and resource.
Before that tenant users can use the app, the corresponding service principal must be register to that tenant first by permission grant. Is API2 is not in the tenant of the users sign-in?
If I understand correctly, we need to specify the knownClientApplications in the manifest of API1(http://application_uri.com/') with the client_id of your SPA, and it also require to set the permission of API1 to the SPA. After that, when the users sign-in your SPA, the API1 app will also register to the users' tenant.
More detail about multi-tier applications please refer the document below:
How to sign in any Azure Active Directory (AD) user using the multi-tenant application pattern
Update( append the test result to explain)
To get this working I had to add the following delegated permissions to API1 for AP2.
Azure Permissions

Resources