I am trying to obtain documentdbclient using the resourcetokens. I have a redis cluster with key as user identity and value as the resourcetoken.
I have a service that uses master key to generate resourcetokens for the user and updates them in Redis. I am using the below code to create resource token in my master service
ResourceResponse<Permission> readPermissions = documentClient.readPermission("/dbs/customerdb/users/mobileuser/permissions/readperm", null);
String accessToken=permission.getToken();
DocumentClient documentClient = new DocumentClient(HOST, MASTER_KEY,
ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);
Then i use below code to get resourcetoken and store it in redis
jedis.put("Client_1",readPermissions .getResource().getToken());
Now, at client side when i try to create documentClient using the resourcetoken
DocumentClient manageClient = new DocumentClient(HOST, jedis.get("Client_1"),ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);
I get logs stating unauthorized and following that the error
Unauthorized, The input authorization token can't serve the request
I have created a user called mobileuser on database customerdb and permission with mode PermissionMode.Read on collection customers
I changed my code to be very sure that the tokens are not getting expired but still getting error
java.lang.IllegalStateException: com.microsoft.azure.documentdb.DocumentClientException: The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get
colls
dxhxakm3caa=
mon, 05 jun 2017 08:56:40 gmt
below id the code that i used to get the token
ResourceResponse<Permission> permissions=documentClient.readPermission("/dbs/customerdd/users/mobileuser/permissions/readperm", null);
System.out.println(permissions.getResource().getResourceLink());
DocumentClient managedClient=new DocumentClient(HOST,permissions.getResource().getToken(), ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);
FeedResponse<Document> response = managedClient.queryDocuments(collection.getResource().getSelfLink(), "SELECT customers.source FROM customers where customers.source='direct-mail'", null);
Iterator<Document> itr = response.getQueryIterator();
while(itr.hasNext()){
Document doc=itr.next();
System.out.println(doc.get("source"));
}
Any pointer will be of a great help
Unauthorized, The input authorization token can't serve the request
As far as I know, the default valid timespan of the resource token is one hour. And if the resource token expires, subsequent requests receive a 401 unauthorized exception, please make sure if the resource token is expired when you retrieve it from Redis cache.
Update:
If i use the overloaded constructor of DocumentClient and pass the PermissionFeed then it works
DocumentClient class has two constructors as below, and when you use new DocumentClient(HOST, jedis.get("Client_1"),ConnectionPolicy.GetDefault(), ConsistencyLevel.Session);, it seems that it recognizes jedis.get("Client_1") that you passed as a string and use the second constructor to initialize a new instance, which would be the cause of the issue.
Related
I have an Azure Web App that authenticates a user which then navigates to a page where some Sharepoint documents are retrieved and displayed in the app.
Most of the time the application works fine, but ocassionally App Insights will highlight that Failed to acquire token silently as no token was found in the cache. Call method AcquireToken. Some users report issues from time to time on this page (it's inconsistent so it might happen a few times a day with a somewhat large user base). The problem is that currently the error isn't handled and I'm trying to figure out how to make the call to AcquireTokenAsync.
The following is the method that returns the token (or doesnt):
private async Task<string> GetUserAccessToken()
{
try
{
// Credentials for app
// _clientId and _clientSecret represent the app info - not shown here in code
ClientCredential credential = new ClientCredential(_clientId, _clientSecret);
//Construct token cache
ITokenCacheFactory cacheFactory = Request.HttpContext.RequestServices.GetRequiredService<ITokenCacheFactory>();
TokenCache cache = cacheFactory.CreateForUser(Request.HttpContext.User);
AuthenticationContext authContext = new AuthenticationContext(_authority, cache);
// guid of the user currently logged into the app
string objectID = _userObjectId;
UserIdentifier userIdentifier = new UserIdentifier(objectID, UserIdentifierType.UniqueId);
string resource = "https://test.sharepoint.com";
AuthenticationResult result = await authContext.AcquireTokenSilentAsync(resource, credential, userIdentifier);
return result.AccessToken;
}
catch (Exception ex)
{
throw ex;
}
}
If I understand the flow correctly, the web app here will request a token using it's own credentials on behalf of the user currently logged in. (Am I right in understanding this based on the method signature which states - Identifier of the user token is requested for. This parameter can be Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier.Any.)
Now when this fails, I would need to make a call to AcquireTokenAsync. There are a number of these methods available and I can't seem to find the one that will fulfill this requirement.
Before the suggestion comes, I can't use AcquireTokenAsync(resource, clientId, redirectUri,new PlatformParameters(PromptBehavior.Auto)); because the constructor on PlatformParameters has changed and requires an implementation of a ICustomWebUi and this flow isn't supported on .Net Core 3.1 as far as I'm aware which makes this unusable.
AcquireTokenAsync(resource, credentials) works and returns a token, however, when using that token I get a 401 Unauthorized when accessing the Sharepoint resources, most likely because the token is different and it is now requested on behalf of the application and not the user logged into the application (if I'm following this train of thought correctly...).
My question is - which method do I call? Is there something I would need to add before making the call to AcquireTokenAsync and if so, which of the 10 or so overloads should I use? I tried using AcquireTokenAsync(resource, credenetial, userAssertion) and passed in the AccessToken that I retrieved on the User logged in, but then I got Assertion failed signature validation or variations on that. If I understood correctly, the UserAssertion can be initialized with 1,2 or 3 parameters and I tried providing the AccessToken currently on the user that is logged in the app, but with no success.
Any help is greatly appreciated as I've been looking at this for two days now.
I spent more time investigating this, but none of the methods available would have worked in my case. The auth flow wasn't an on-behalf-of flow, but an auth-code flow. The link is to the newer MSAL library, but the concept is there. The application, a .net core web app, directs the user to sign in. When they sign in, an auth-code is passed into the response once they successfully authenticate.
The auth-code is then used to call AcquireTokenByAuthorizationCodeAsync(AuthCode, Uri, ClientCredential, UserIdentifier). This returns the valid access token that can be stored in the distributed token cache and then used to authenticate in order to access a given resource.
My biggest issue was that the error says you need to use AcquireTokenAsync to retrieve a new token. This is correct to a certain point, because in order to make any calls to any of the 14 or so methods you will need different bits of information, which will be dependent on the way you have setup your authentication flow in your application.
Because the application I worked on used auth code flow, I would need to get a new auth code. This would mean redirecting the user to login, capture the auth code in the response if the login was successful and then call the appropriate AcquireTokenAsync method that takes in an auth code as parameter along with app info, uri and so on.
To solve this, I used the information provided by the Microsoft Github page on Acquiring tokens with auth codes in web apps. Here I found samples on how auth flow is setup, but most importantly, how to trigger a new authentication flow if the user needs to be re-authenticated.
I wrapped the code that would throw the AdalSilentTokenAcquisitionException, catch the error and return a RedirectToAction.
return RedirectToAction("ActionName", "Controller", new RouteValues);
The above redirects the user to a given action, in a particular controller and passes through an object that can hold additional parameters. In my case it's a new { redirectUri = redirectUriString}, which is a string object that holds the URL the user would try to navigate this. I constructed this with a little method that uses the current HttpRequest to find the url the user was trying to get to.
Next, the controller that I had setup which responds to that redirect:
[HttpGet("/SignIn")]
public IActionResult SignIn([FromQuery(Name ="redirectUri")]string redirectUri)
{
return Challenge
(
new AuthenticationProperties { RedirectUri = WebUtility.UrlDecode(redirectUri) },
OpenIdConnectDefaults.AuthenticationScheme
);
}
Here, a Challenge is returned. The challenge triggers a call to the authentication flow that was setup in the Startup class. I think the entire flow here is that the method will send people to go through whatever is in that startup, which, in the case of the application I worked on, it prompts the user to sign in, captures the auth code, requests a new access token and once this is received and saved in the distributed token cache, the user is redirected to the redirectUri that I passed through.
I hope this helps or at least gives a starting point to anyone who might encounter a similar issue.
I have a protected service, but I need to create links for sharing purpose. So I came over this feature:
new ApiKeyAuthProvider(AppSettings) {
AllowInHttpParams=true
},
I'm calling the service, getting the API directly from the ApiKey table, and in the debug console I can even see the SQL, which is correct (select where id, and id is my api key) and matches an active user, but still I'm getting a 403 from ServiceStack.
The apikey query param is used. Https is used (with valid CA signed cert).
403 Forbidden indicates Authentication was successful (otherwise would return 401 unauthorized) but the authenticated user does not have access to the resource, e.g. they don't have the required roles or permissions.
I have a docusign-sandbox account. I am trying to integrate docusign with my application and am using JWT Grant for authentication in java. I got a sample code from https://github.com/docusign/eg-01-java-jwt and it works perfectly for an hour and then the API starts failing.
Any idea how I can tackle this issue?
I'm getting the below error
I already tried changing the Token expiry time from 1hr to other lesser values(5 min, 30 min). Even then the APIs start failing exactly after an hour.
https://github.com/docusign/eg-01-java-jwt
ERROR MESSAGE
{"timestamp":1560750467288,"status":500,"error":"Internal Server Error","message":"Error while requesting server, received a non successful HTTP code 401 with response Body: '{\r\n \"errorCode\": \"USER_AUTHENTICATION_FAILED\",\r\n \"message\": \"One or both of Username and Password are invalid. Invalid access token\"\r\n}'","path":"{path}"}
Found a way around the problem.
The access token was being generated but for some reason it was not updating the token in the ApiClient Object and was using the old token only.
So now I am just creating a new ApiClient Object every time the token expires instead of replacing the old token with the new one.
The jwt grant returns an access token that is only valid for 1 hour. After that, you need to generate a new token for another hour.
Call the example's checkToken method before each API call. It should create a new access token as needed.
Added
You'll need to debug to see what's happening. Is the checkToken method obtaining a new access token after 50 minutes (it should be using a 10 minute buffer time). Is the new access token being used?
I have been going through some of my .NET Core2 services and adding some JWT authentication to them to provide some basic security.
I created a new ProvisioningService which has an endpoint that builds a token and returns it:
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
I altered one of my existing services (which I'll refer to as TestService) by adding AddAuthentication in the StartUp. The endpoint for this call has the [HttpPost(), Authorize] attributes. I deployed these changes to my Test server.
When I call TestService/api/updateSomething I am returned a 401 Unauthorized as expected. On my local machine, I create a new token via ProvisioningService/api/buildToken and add the token from the response to my TestService call via the Authorization header. To my surprise...this worked.
Why does my TestService (on a completely different server) view a token created on my local machine as a valid token and allow the call to work? I was expecting this to return the same 401 because I assumed this token was going to be invalid on my Test server. My inexperience with JWT is probably showing....but I am not understanding how these tokens are being stored/shared between servers.
I failed to understand that the token itself has what it needs to authorize itself after it is decrypted. This question is no longer needed.
Azure access token is returned even with an invalid secret when run in a multithreaded environment.
I've got an integration test that checks to see that an invalid client secret won't pass when getting an Azure access token.
When run in isolation the test passes every time, meaning that an invalid client secret does not return an Azure access token.
However, when run with other integration tests (on multiple threads) this function returns an access token even with an obviously invalid client secret.
I don't see any legitimate reason this would be a cached token for the client id even when specifying a totally invalid client secret.
Note, this behavior does not happen when the client id is invalid.
Is there an explanation for this behavior?
private async Task<string> GetAccessToken(string authority, string resource, string scope)
{
var clientCredential = new ClientCredential(clientId, clientSecret);
var context = new AuthenticationContext(authority, TokenCache.DefaultShared);
var result = await context.AcquireTokenAsync(resource, clientCredential);
Debug.WriteLine("----------------------------------");
Debug.WriteLine(clientId);
Debug.WriteLine(clientSecret);
Debug.WriteLine(result.AccessToken);
return result.AccessToken;
}
The debug output is
Debug Trace:
----------------------------------
<...client id...>
invalid secret
<...valid token...>
This is because your cache still has a valid access token in the cache. ADAL checks the cache first and returns the access token if still valid (not expired). Token cache pivots on client_id as one of the dimensions of the key, so invalid client_id fails as expected. To force the library to use the secret and make a network call, you must delete the token from the cache
Clear client credential token like below. This AuthenticationContext cache the credentials. clear them before checking another pair of keys.
var authenticationContext = new AuthenticationContext($"https://login.microsoftonline.com/{tenantId}/v2.0");
if (authenticationContext?.TokenCache.Count > 0)
{
authenticationContext.TokenCache.Clear();
}