1

I'm trying to connect to my cosmos db database by using a Resource token instead of a account master key which I used before. Before I used the following way to create DocumentClient:

    var client = new DocumentClient(new Uri(configAccountName), configAccountKey);
    await client.OpenAsync();

This way works well, but I want to use resource token ways for that target as mentioned in these articles : link1 and link2.

I've created a db user with All permissions by the following code:

    public static async Task<User> CreateUserAndPermissionAsync(this DocumentClient client, string userId)
    {
        var dbUri = UriFactory.CreateDatabaseUri(dataBase);
        var user = await client.CreateUserAsync(dbUri, new User { Id = userId });
        var collectionUri = UriFactory.CreateDocumentCollectionUri(dataBase, collectionName);
        var permission = await client.CreatePermissionAsync(
            user.Resource.SelfLink,
            new Permission
            {
                Id = "MyPermission",
                PermissionMode = PermissionMode.All,
                ResourceLink = collectionUri.ToString(),
                ResourcePartitionKey = new PartitionKey(userId)
            });
        return user.Resource;
    }

The user is created correctly and I can retrieve him by ReadUserAsync or his permissions by ReadPermissionAsync methods. Then, I want to create new instance of DocumentClient with permissions of newly created user.

    //this is a temporally document client instance to read permissions for my user in below extensions method versions
    var client = new DocumentClient(new Uri(configAccountName), configAccountKey);

I've checked 3 ways (some of them are similar). I've tryied to call each of ways to get new DocumentClient instance(newClient variable) and then call OpenAsync method to open connection:

    var newClient = await client.GetClientForUserAsync_v###(userName);
    await newClient.OpenAsync();

All tryings were failed

    //version 1:
    public static async Task<DocumentClient> GetClientForUserAsync_v1(this DocumentClient client, string userId)
    {
        var userUri = UriFactory.CreateUserUri(dataBase, userId).ToString();
        var permissionsUri = $"{userUri}/permissions";
        var permissions = (await client.ReadPermissionFeedAsync(permissionsUri)).ToList();
        return new DocumentClient(
            client.ServiceEndpoint,
            permissions,
            client.ConnectionPolicy);
    }

The OpenAsync method was crashed with this error : Failed to parse the value '' as ResourceId., documentdb-dotnet-sdk/1.22.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0

    //version 2:
    public static async Task<DocumentClient> GetClientForUserAsync_v2(this DocumentClient client, string userId)
    {
        var userUri = UriFactory.CreateUserUri(dataBase, userId).ToString();
        var permissionsUri = $"{userUri}/permissions";
        var permissions = (await client.ReadPermissionFeedAsync(permissionsUri)).ToList();
        return new DocumentClient(
            client.ServiceEndpoint,
            permissions[0].Token,
            client.ConnectionPolicy);
    }

The OpenAsync method was crashed with this error : Insufficient permissions provided in the authorization header for the corresponding request. Please retry with another authorization header. ActivityId: ##ReplacedActivityId##, Microsoft.Azure.Documents.Common/1.22.0.0, documentdb-dotnet-sdk/1.22.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0

    //version 3:
    public static async Task<DocumentClient> GetClientForUserAsync_v3(this DocumentClient client, string userId)
    {
        FeedResponse<Permission> permFeed = await client.ReadPermissionFeedAsync(UriFactory.CreateUserUri(dataBase, userId));
        List<Permission> permList = new List<Permission>();

        foreach (Permission perm in permFeed)
        {
            permList.Add(perm);
        }

        DocumentClient userClient = new DocumentClient(new Uri(client.ServiceEndpoint.AbsoluteUri), permList, new ConnectionPolicy()
        {
            //**UPDATE**: I tried all ConnectionMode values as well as without this parameter
            ConnectionMode = ConnectionMode.Gateway
        });
        return userClient;
    }     

The OpenAsync method was crashed with this error : Failed to parse the value '' as ResourceId., documentdb-dotnet-sdk/1.22.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0.

The first and third errors are the most strange, because I've checked and I see the ResourceId value in the perm variable (as item of permList collection) as well as in permissions variable from the first version.

Could anyone help with it?

UPDATE I've checked the 4th version, which gives me the same result as 1,3 trying: Failed to parse the value '' as ResourceId., documentdb-dotnet-sdk/1.22.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0

    //version 4
    public static async Task<DocumentClient> GetClientForUserAsync_v4(this DocumentClient client, string userId)
    {
        var user = await client.ReadUserAsync(UriFactory.CreateUserUri(dataBase, userId));
        var permissions = await client.ReadPermissionFeedAsync(user.Resource.SelfLink);
        List<Permission> permList = new List<Permission>();

        foreach (Permission perm in permissions)
        {
            permList.Add(perm);
        }

        DocumentClient userClient = new DocumentClient(new Uri(client.ServiceEndpoint.AbsoluteUri), permList, new ConnectionPolicy()
        {
            ConnectionMode = ConnectionMode.Direct
        });
        return userClient;
    }
4
  • do you still have the same issue ? Commented Oct 11, 2018 at 22:28
  • @WalidAshraf, yep. Commented Oct 11, 2018 at 22:44
  • @dododo Have you found any resolution for the above issue? I am facing the same issue. Commented Feb 4, 2020 at 9:58
  • @Darey, no :( But I don't work on this issue anymore. Commented Feb 4, 2020 at 13:07

3 Answers 3

0

The Cosmos DB resource token will be generated when the permission been built. We can use resource token to connect to Cosmos DB like below:

var client = new DocumentClient(new Uri(endPointUri), resourceToken );

And we can get resource Token of the permission as below:

    public static async Task<Dictionary<PermissionMode, string>> GetPermissonTokens(DocumentClient client,string DatabaseId,string userId)
    {
        FeedResponse<Permission> permFeed =  await client.ReadPermissionFeedAsync(UriFactory.CreateUserUri(DatabaseId, userId));

        Dictionary<PermissionMode,string> permList = new Dictionary<PermissionMode, string>();

        foreach (Permission perm in permFeed)
        {

            permList.Add(perm.PermissionMode, perm.Token);
        }

        return permList;
    }

Here is a Complete demo for your reference:

    static void Main(string[] args)
    {
        //create DocumentClient instance with end point Uri and primary key
        DocumentClient client = new DocumentClient(new Uri("https://xxxx.xxxxx.azure.com:443/"), "ikhSrMZIGKBrF1xxxxx7iDSLBMJD37oCOlw2N24YoBtfAV7HJFfVgNbhCfQdWGAq3eZY4FyX3z6zsWoRLHQ==", new ConnectionPolicy { EnableEndpointDiscovery = false });

        Task<Dictionary<PermissionMode, string>> task = GetPermissonTokens(client, "Test2", "testUser2");
        task.Wait();
        Dictionary<PermissionMode, string> permissionTokens = task.Result;

        DocumentClient userClient = new DocumentClient(client.ReadEndpoint, permissionTokens[PermissionMode.All]);
        if (userClient != null)
        {
            //then we can do CRUD operation to Cosmos DB Here 
        }
    }


    public static async Task<Dictionary<PermissionMode, string>> GetPermissonTokens(DocumentClient client,string DatabaseId,string userId)
    {
        FeedResponse<Permission> permFeed =  await client.ReadPermissionFeedAsync(UriFactory.CreateUserUri(DatabaseId, userId));

        Dictionary<PermissionMode,string> permList = new Dictionary<PermissionMode, string>();

        foreach (Permission perm in permFeed)
        {

            permList.Add(perm.PermissionMode, perm.Token);
        }

        return permList;
    }
Sign up to request clarification or add additional context in comments.

3 Comments

Hi @Lee Liu, I've checked your solution and the result of userClient.OpenAsync().Wait() method is the same as in my version 2. I have the following error : Insufficient permissions provided in the authorization header for the corresponding request. Please retry with another authorization header. ActivityId: ##ReplacedActivityId##, Microsoft.Azure.Documents.Common/1.22.0.0, documentdb-dotnet-sdk/1.22.0 Host/32-bit MicrosoftWindowsNT/6.2.9200.0.
Also, I would like to highlight that I use Read and Write Primary key from azure portal as accountKey. I didn't configure any other portal settings. Maybe I need to set some additional settings in Azure Cosmos db settings in azure portal?
@LeuLiu, just in case if previous way doesn't trigger notification
0

I have similar case where in my model I am using partitioning. So, I have developed this function to be get the user token.

    private async Task<string> GetUserToken()
    {
        User user = null;
        try
        {
            try
            {
                var uri = UriFactory.CreateUserUri(config.Database, config.PartitionKey);
                client.DeleteUserAsync(uri).Wait();

                user = await client.ReadUserAsync(UriFactory.CreateUserUri(config.Database, config.PartitionKey));

                var permission = await GetorCreatePermission(user, config.Collection, config.PartitionKey);
                return permission.Token;
            }
            catch (Exception ex) {
                Console.WriteLine(ex.Message);
            }
            if (user == null)
            {
                user = new User
                {
                    Id = config.PartitionKey
                };
                user = await client.CreateUserAsync(UriFactory.CreateDatabaseUri(config.Database), user);
                var permission = await GetorCreatePermission(user, config.Collection, config.PartitionKey);
                return permission.Token;
            }
            else
            {
                throw new Exception("");
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

Comments

0

After multiple attempts, the one conclusion I ended up was not to include the PartitionKey when creating permission for the collection. The below tweak did the job for me and I am able to successfully add contents to my collection using resource tokens.

    new Permission
    {
        Id = "MyPermission",
        PermissionMode = PermissionMode.All,
        ResourceLink = collectionUri.ToString()
    });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.