Azure DocumentDB: Time-bound resource token remains valid after expiration time - azure

I'm facing a strange problem in DocumentDb.
I created a user and permission for manipulating a specific collection. Then I requested a resource token with a limited validity for that user. I did this by setting ResourceTokenExpirySeconds to say 10 seconds. To test it, I waited enough in order the token got expired. However, Azure accepted subsequent requests using that (supposedly expired) token.
Considering the following code, it is expected the this line fails but it doesn't:
UserPermissionExample.TestToken(10, 15);
And here is UserPermissionExample implementation:
public class UserPermissionExample
{
const string endPoint = "https://[ENDPOINT].documents.azure.com";
const string masterKey = "[PRIMARY-KEY]";
const string dbName = "test-db";
const string testUserId = "test-user";
const string collName = "test-coll";
public static async Task TestToken(int validity, int wait)
{
// Get a time-bound token
string token = await GetToken(validity /*seconds*/);
// Wait enough so the token gets expired
Thread.Sleep(TimeSpan.FromSeconds(wait));
// Try to add a document with the expired token. It is expected to fail:
DocumentClient client = new DocumentClient(new Uri(endPoint), token);
await client.CreateDocumentAsync(
UriFactory.CreateDocumentCollectionUri(dbName, collName),
new { name = "A test document" });
}
static async Task<string> GetToken(int tokenValiditySeconds)
{
DocumentClient client = new DocumentClient(new Uri(endPoint), masterKey);
ResourceResponse<Database> db = await UpsertDb(client);
ResourceResponse<DocumentCollection> collection = await UpsertCollection(client, db);
var userUrl = UriFactory.CreateUserUri(dbName, "testuser");
var user = await client.UpsertUserAsync(db.Resource.SelfLink, new User() { Id = testUserId });
var permission =
await client.UpsertPermissionAsync(
user.Resource.SelfLink,
new Permission()
{
Id = "PersmissionForTestUser",
PermissionMode = PermissionMode.All,
ResourceLink = collection.Resource.SelfLink,
},
new RequestOptions() { ResourceTokenExpirySeconds = tokenValiditySeconds });
permission =
await client.ReadPermissionAsync(
permission.Resource.SelfLink,
new RequestOptions() { ResourceTokenExpirySeconds = tokenValiditySeconds });
return permission.Resource.Token;
}
static async Task<ResourceResponse<DocumentCollection>> UpsertCollection(DocumentClient client, ResourceResponse<Database> db)
{
ResourceResponse<DocumentCollection> collection = null;
try
{
collection = await client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(dbName, collName));
}
catch (DocumentClientException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
collection = await client.CreateDocumentCollectionAsync(db.Resource.SelfLink, new DocumentCollection() { Id = collName });
}
}
return collection;
}
static async Task<ResourceResponse<Database>> UpsertDb(DocumentClient client)
{
ResourceResponse<Database> db = null;
try
{
db = await client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(dbName));
}
catch (DocumentClientException ex)
{
if (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
db = await client.CreateDatabaseAsync(new Database() { Id = dbName });
}
}
return db;
}
}
PS: I'm using Microsoft.Azure.DocumentDB version="1.11.4".
Update: I found that Azure starts checking token expiry from 5 minutes after token issue time ! Then it rejects the token. So prior to this period, no matter token is expired or not, Azure accepts it!

The reason for this is that DocumentDB has some slack in the token expiry to allow for potential clock skews in the client or server. Because of this tokens have a "grace period" after their real expiry time. The allowed clock skew is in the tune of 5 minutes which is why you see the behaviour that you do.
Is there a particular reason you're creating a token that should be valid only for 10 seconds?
Full disclosure: I work at DocumentDB.

Related

How to handle Sql Server rollbacks when Azure service bus message fails to save on the queue and they depend on each other?

I'm saving a row to my db (class with teacher/students, time, date, etc), once I have the id I create a message on my Azure service bus where the unique id of the row from my db is used as the message body of the service bus message. I'm creating scheduled messages so I can notify the students before the class and after the class is over so they can rate/review their teacher.
QUESTION - I'd like to know how to roll back or an easy way to remove the db row by not allowing it to fully save if the message to the Azure service bus fails to save?
Currently I'm using a generic repository with UnitOfWork to save to my db and I'm catching the exception from my service bus service if it fails, then deleting the row that was just saved, but it's sloppy looking and I can see it will lead to problems.
Here is what I'm doing now in the controller.
[HttpPost("create")]
public async Task<IActionResult> OnCreating(OnCreatingEventDto onCreatingDto)
{
var userFromRepo = await _userManager.FindByEmailFromClaimsPrinciple(HttpContext.User);
if (userFromRepo == null)
return Unauthorized(new ApiResponse(401));
var newEvent = _mapper.Map<ClassEvent>(onCreatingDto);
_unitOfWork.Repository<ClassEvent>().Add(newEvent);
var success = await _unitOfWork.Complete();
if (success > 0) {
try {
var sequenceNUmber = await _serviceBusProducer.SendMessage(newEvent.Id.ToString(), newEvent.eventTime.addDays(1), queueName);
newEvent.ServiceBusSequenceNumber = sequenceNUmber;
_unitOfWork.Repository<ClassEvent>().Update(newEvent);
var secondSuccess = await _unitOfWork.Complete();
if (secondSuccess > 0) {
return Ok();
}
} catch(Exception ex) {
_logger.LogError("error saving to service bus");
_unitOfWork.Repository<ClassEvent>().Delete(newEvent);
var deleteSuccess = await _unitOfWork.Complete();
if (deleteSuccess > 0) {
}
return BadRequest(new ApiResponse(400, "Problem Creating Event"));
}
}
return BadRequest(new ApiResponse(400, "Problem Creating Event"));
}
Here is the method from my service that creates the message on the queue
public async Task<long> SendMessage(string messageBody, DateTimeOffset scheduledEnqueueTime, string queueName)
{
await using (ServiceBusClient client = new ServiceBusClient(_config["ServiceBus:Connection"]))
{
ServiceBusSender sender = client.CreateSender(_config["ServiceBus:" + queueName]);
ServiceBusMessage message = new ServiceBusMessage(messageBody);
var sequenceNumber =
await sender.ScheduleMessageAsync(message, scheduledEnqueueTime);
return sequenceNumber;
}
}

Authentication problem - secure web application with API auth server

I have started to create a software architecture where i have:
Auth_API - as an auth server
Resource_API - as a resource API (protected with Auth_API)
WebApplication (mvc) - a frontend application (protected with Auth_API).
Based on https://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/ article I have successfully made a Google authentication.
WebApp == redirects to ==> Auth_API == challenge ==> Google ==> API receives externalAccessToken, registers user locally and returns localAccessToken
Now everything is OK when I would want to use bearer authorization (using local access token).
But I also want to sign in to by ASP MVC application with (cookie?) ClaimsIdentity.
I was thinking about switching to JWT, but I am not sure which way I should go...
Bit of code:
Auth_API - obtain local access token
/// <summary>
/// Returns local access token for already registered users
/// </summary>
/// <param name="provider"></param>
/// <param name="externalAccessToken"></param>
/// <returns></returns>
[AllowAnonymous]
[HttpGet]
[Route("ObtainLocalAccessToken")]
public async Task<IHttpActionResult> ObtainLocalAccessToken(string provider, string externalAccessToken)
{
if (string.IsNullOrWhiteSpace(provider) || string.IsNullOrWhiteSpace(externalAccessToken))
{
return BadRequest("Provider or external access token is not sent");
}
var verifiedAccessToken = await VerifyExternalAccessToken(provider, externalAccessToken);
if (verifiedAccessToken == null)
{
return BadRequest("Invalid Provider or External Access Token");
}
IdentityUser user = await _repo.FindAsync(new UserLoginInfo(provider, verifiedAccessToken.user_id));
bool hasRegistered = user != null;
if (!hasRegistered)
{
return BadRequest("External user is not registered");
}
//generate access token response
var accessTokenResponse = GenerateLocalAccessTokenResponse(user.UserName);
return Ok(accessTokenResponse);
}
Generate Local Access Token algorythm
private JObject GenerateLocalAccessTokenResponse(string userName)
{
var tokenExpiration = TimeSpan.FromDays(1);
ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, userName));
identity.AddClaim(new Claim("role", "user"));
var props = new AuthenticationProperties()
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration),
};
var ticket = new AuthenticationTicket(identity, props);
var accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket);
JObject tokenResponse = new JObject(
new JProperty("userName", userName),
new JProperty("access_token", accessToken),
new JProperty("token_type", "bearer"),
new JProperty("expires_in", tokenExpiration.TotalSeconds.ToString()),
new JProperty(".issued", ticket.Properties.IssuedUtc.ToString()),
new JProperty(".expires", ticket.Properties.ExpiresUtc.ToString())
);
return tokenResponse;
}
Web application - part of login action:
if (hasLocalAccount)
{
var client = new RestClient(baseApiUrl);
var externalLoginUrl = "Account/ObtainLocalAccessToken";
var externalLoginRequest = new RestRequest(externalLoginUrl, Method.GET);
externalLoginRequest.AddQueryParameter("provider", provider);
externalLoginRequest.AddQueryParameter("externalAccessToken", externalAccessToken);
var externalLoginResponse = client.Execute(externalLoginRequest);
if (externalLoginResponse.IsSuccessful)
{
JObject response = JObject.Parse(externalLoginResponse.Content);
string localAccessToken = response["access_token"].Value<string>();
string localTokenExpiresIn = response["expires_in"].Value<string>();
// WHAT TO DO WHERE TO SIGN IN A USER ???
//AuthenticationTicket ticket = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(localAccessToken); <== this returns NULL
return RedirectToAction("Index", "Home");
}
}

Azure DocumentDB, When uploading af executing - no result?

In my project I am supposed to get data from openweathermap.org and put that in a collection in my DocumentDB database in Azure.
The code below works locally on my development machine, but when i upload the project, it runs and succeed (says the dashboard) but no documents are created. I can only create the documents if I run from local machine.
Why is that?
Here is my code:
public static void Main()
{
JobHost host = new JobHost();
// The following code ensures that the WebJob will be running continuously
host.Call(typeof(Program).GetMethod("saveWeatherDataToAzureDocumentDB"));
}
[NoAutomaticTrigger]
public static async void saveWeatherDataToAzureDocumentDB()
{
string endpointUrl = ConfigurationManager.AppSettings["EndPointUrl"];
string authorizationKey = ConfigurationManager.AppSettings["AuthorizationKey"];
string url = "http://api.openweathermap.org/data/2.5/weather?q=hanstholm,dk&appid=44db6a862fba0b067b1930da0d769e98";
var request = WebRequest.Create(url);
string text;
var response = (HttpWebResponse)request.GetResponse();
using (var sr = new StreamReader(response.GetResponseStream()))
{
text = sr.ReadToEnd();
}
// Create a new instance of the DocumentClient
var client = new DocumentClient(new Uri(endpointUrl), authorizationKey);
// Check to verify a database with the id=FamilyRegistry does not exist
Database database = client.CreateDatabaseQuery().Where(db => db.Id == "weatherdata").AsEnumerable().FirstOrDefault();
// If the database does not exist, create a new database
if (database == null)
{
database = await client.CreateDatabaseAsync(
new Database
{
Id = "weatherdata"
});
}
// Check to verify a document collection with the id=FamilyCollection does not exist
DocumentCollection documentCollection = client.CreateDocumentCollectionQuery(database.SelfLink).Where(c => c.Id == "weathercollection").AsEnumerable().FirstOrDefault();
// If the document collection does not exist, create a new collection
if (documentCollection == null)
{
documentCollection = await client.CreateDocumentCollectionAsync("dbs/" + database.Id,
new DocumentCollection
{
Id = "weathercollection"
});
}
//Deserialiser til et dynamisk object
if (text == "")
{
mark m = new mark() { name = "Something" };
await client.CreateDocumentAsync(documentCollection.DocumentsLink, m);
}
else
{
var json = JsonConvert.DeserializeObject<dynamic>(text);
json["id"] = json["name"] + "_" + DateTime.Now;
await client.CreateDocumentAsync(documentCollection.DocumentsLink, json);
}
}
public sealed class mark
{
public string name { get; set; }
}
UPDATE - This is what I have in my App.config
<appSettings>
<!-- Replace the value with the value you copied from the Azure management portal -->
<add key="EndPointUrl" value="https://<My account>.documents.azure.com:443/"/>
<!-- Replace the value with the value you copied from the Azure management portal -->
<add key="AuthorizationKey" value="The secret code from Azure"/>
Also, At DocumentDB Account i find the Connection string like this. AccountEndpoint=https://knoerregaard.documents.azure.com:443/;AccountKey=my secret password
How should I apply this to the WebJob?
Appriciate your help!

Is OrganizationServiceProxy Connected?

What is the best way to tell if an OrganizationServiceProxy has successfully connected to CRM?
I am using GetEnumerator() on AccountSet as this fails if not connected.
/* Tries to connect to CRM and return false if failure - credentials arguments */
public bool Connect(string username, string password, string uri)
{
try
{
var cred = new ClientCredentials();
cred.UserName.UserName = username;
cred.UserName.Password = password;
service = new OrganizationServiceProxy(new Uri(uri), null, cred, null);
service.EnableProxyTypes(); // Allow LINQ early bound queries
linq = new Context(service);
/* This is where I need help */
var e = linq.AccountSet.GetEnumerator(); // this fails if not connected
}
catch
{
return false;
}
return true;
}
Service and Linq are private fields.
Context is the serviceContextName in crmsvcutil.exe.
I am in the habit of using the name "linq" for the Context object.
There must be a better way.
The simplest way is to execute a WhoAmIRequest, this because when you connect to CRM you need to provide valid credentials.
If the credentials are correct the WhoAmIRequest will return the current user GUID, if are not correct the request will fail.
So your code can be:
public bool Connect(string username, string password, string uri)
{
try
{
var cred = new ClientCredentials();
cred.UserName.UserName = username;
cred.UserName.Password = password;
service = new OrganizationServiceProxy(new Uri(uri), null, cred, null);
WhoAmIRequest request = new WhoAmIRequest();
WhoAmIResponse response = (WhoAmIResponse)service.Execute(request);
Guid userId = response.UserId;
}
catch
{
return false;
}
return true;
}

Obtaining Access token from refresh token using Google API

I have been using google+ api for .NET in my application.Using the example provided in this
site i am able to get the access token.
My problem is how to obtain the access token from the refresh token using OAuth 2.0.I haven't found any examples to get the access token from refresh token.
I have referred the [google+ API reference] but they have mentioned it using HTTP methods.2Please provide me some examples in C# using the methods provided by google+ APIs.
For the first time, you need to get the access token from the browser prompt and then save it in some store.
Check if the token is expired and then try to refresh it.
Code here :
private static IAuthorizationState GetAuthentication(NativeApplicationClient arg)
{
try
{
// Get the auth URL:
var config = new Configuration();
var calendarScope = Google.Apis.Util.Utilities.ConvertToString(CalendarService.Scopes.Calendar);
IAuthorizationState state = new AuthorizationState(new[] { calendarScope });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
var authCode = String.Empty;
if (String.IsNullOrWhiteSpace(config.AccessToken) || config.AccessTokenExpirationTime < DateTime.Now || String.IsNullOrWhiteSpace(config.RefreshToken))
{
// Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString());
do
{
authCode = Prompt.ShowDialog("Test", "123");
} while (String.IsNullOrWhiteSpace(authCode));
state = arg.ProcessUserAuthorization(authCode, state);
}
else
{
state.AccessToken = config.AccessToken;
state.AccessTokenExpirationUtc = config.AccessTokenExpirationTime;
state.AccessTokenIssueDateUtc = config.AccessTokenIssueTime;
state.RefreshToken =config.RefreshToken ;
if (state.AccessTokenExpirationUtc < DateTime.Now)
{
var tokenRefreshed = arg.RefreshToken(state);
if (tokenRefreshed)
{
config.AccessToken = state.AccessToken;
config.AccessTokenExpirationTime = state.AccessTokenExpirationUtc;
config.AccessTokenIssueTime = state.AccessTokenIssueDateUtc;
config.RefreshToken = state.RefreshToken;
arg.ProcessUserAuthorization(authCode, state);
}
else
{
throw new ApplicationException("Unable to refresh the token.");
}
}
}
return state;
}
catch (System.Exception exp)
{
throw new ApplicationException("Failed to get authorisation from Google Calender.", exp);
}
}

Resources