I am building a REST service as part of my rails app, and I'm wondering if it is bad to expose the ID with a unique hash or timestamp appended to it for each resource.
What I want to achieve is that the client will then know if it has the same resource as the server, and if not, then update it.
An example:
the event resource would look like this in JSON
event: {
id: 123-kjkjlhhkh,
name: event-name,
date: somedate,
users: [456-sadasdasdas, 242-asfat4fdhs]
}
the client would have an event table and a user table, that would look something like this:
Events id | id-hash | name | date
--------------------------------------------------
123 | kjkjlhhkh | event-name | somedate
Users id | id-hash | username
--------------------------------------------
456 | sadasdasdas | oldusername
242 | kkskksksk | someusername
This means that we see on the client side that the user 242 has a new hash appended to the id, which means that the user resource has changed on the server and we can go grab it.
So the question is, is it better(more RESTful) to send the whole user resource (for all the users) together with the event, or to just send the ID:s with appended hashes and get the whole resources from the server in a separate call if they are different than the local ones?
I wouldn't mangle the IDs.
Technically, resources are identified by their URL. So if you have a different URL, you have a different resource. Also, having arbitrary IDs attached makes discovering and using your URLS really hard.
What you should do instead is to use ETags instead. These are typically used for caching of resources. An etag should identify a resource version so that a client use a request header like this:
If-None-Match: "686897696a7c876b7e"
to only get the newest resource if the server version doesn't match the provided ETag. In your case, you could just use your ID hash as an Etag. That way, you use standard HTTP mechanisms and don't have to reinvent the wheel.
Related
I have an existing user in my Xamarin Forms app whose Details are as follows:
First Name: Jim
Last Name: Smith
ProviderLoginId = jsmith#google.com
OAuth Provider: Google
When I first create this user, I use the following method to authenticate against google as follows:
result = await AuthenticationClient.AcquireTokenInteractive("https://***.onmicrosoft.com/profile.read/profile.read")
.ExecuteAsync();
JObject objToken = new JObject();
objToken.Add("access_token", result.IdToken);
MobileServiceUser user = await App.syncMgr.CurrentClient.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, objToken);
Now, I take the UserId property from the MobileServiceUser user variable and store it into my User Table as primary key.
Subsequently, I create another new user with the following details:
First Name: Jim
Last Name: Smith
ProviderLoginId = jsmith#hotmail.com
OAuthProvider: Microsoft
When I create the user using the same steps as above, I get the same UserId back from Azure. Obviously, I am not able to store it in my User table for the second user. This only happens when the first name and the last name are the same regardless of which ProviderLoginId was used (whether it was Google OAuth or Microsoft OAuth).
Should I not be getting a unique UserId in each case? Since the bug surfaces only when the first and last names are same, I am assuming it is some sort of a bug.
Any and all help is appreciated.
You are using the older client, which is no longer supported.
You should be submitting the access token to the service, not the IdToken. You haven't stated what the AuthenticationClient is, but hopefully it is ADAL (since the older service doesn't work with MSAL).
The newer client and service completely abstracts the authentication code, allowing you to use whatever authentication mechanism you like and just dealing with standard OAuth2/OIDC and bearer tokens (Authorization header) instead of the non-standard X-ZUMO-AUTH header that was used in the past. My recommendation is to upgrade the client and server.
Cosmos DB, API Azure Tables, gives you 2 endpoints in the Overview blade
Document Endpoint
Azure Table Endpoint
An example of (1) is
https://myname.documents.azure.com/dbs/tempdb/colls
An example of (2) is
https://myname.table.cosmosdb.azure.com/FirstTestTable?$filter=PartitionKey%20eq%20'car'%20and%20RowKey%20eq%20'124'
You can create the authorization code for (1) on the client using the prerequest code from this Postman script: https://github.com/MicrosoftCSA/documentdb-postman-collection/blob/master/DocumentDB.postman_collection.json
Which will give you a code like this:
Authorization: type%3Dmaster%26ver%3D1.0%26sig%3DavFQkBscU...
This is useful for playing with the rest urls
For (2) the only code I could find to generate a code that works was on the server side and gives you a code like this:
Authorization: SharedKey myname:JXkSGZlcB1gX8Mjuu...
I had to get this out of Fiddler
My questions
(i) Can you generate a code for case (2) above on the client like you can for case (1)
(ii) Can you securely use Cosmos DB from the client?
If you go to the Azure Portal for a GA Table API account you won't see the document endpoint anymore. Instead only the Azure Table Endpoint is advertised (e.g. X.table.cosmosdb.azure.com). So we'll focus on that.
When using anything but direct mode with the .NET SDK, our existing SDKs when talking to X.table.cosmosdb.azure.com endpoint are using the SharedKey authentication scheme. There is also a SharedKeyLight scheme which should also work. Both are documented in https://learn.microsoft.com/en-us/rest/api/storageservices/authentication-for-the-azure-storage-services. Make sure you read the sections specifically on the Table Service. The thing to notice is that a SharedKey header is directly tied to the request it is associated with. So basically every request needs a unique header. This is useful for security because it means that a leaked header can only be used for a limited time to replay a specific request. It can't be used to authorize other requests. But of course that is exactly what you are trying to do.
An alternative is the SharedKeyLight header which is a bit easier to implement as it just requires a date and the a URL.
But we don't have externalized code libraries to really help with either.
But there is another solution that is much friendly to things like Fiddler or Postman, which is to use a SAS URL as defined in https://blogs.msdn.microsoft.com/windowsazurestorage/2012/06/12/introducing-table-sas-shared-access-signature-queue-sas-and-update-to-blob-sas/.
There are at least two ways to get a SAS token. One way is to generate one yourself. Here is some sample code to do that:
var connectionString = "DefaultEndpointsProtocol=https;AccountName=tableaccount;AccountKey=X;TableEndpoint=https://tableaccount.table.cosmosdb.azure.com:443/;";
var tableName = "ATable";
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connectionString);
CloudTableClient tableClient = storageAccount.CreateCloudTableClient();
CloudTable table = tableClient.GetTableReference(tableName);
await table.CreateIfNotExistsAsync();
SharedAccessTablePolicy policy = new SharedAccessTablePolicy()
{
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(1000),
Permissions = SharedAccessTablePermissions.Add
| SharedAccessTablePermissions.Query
| SharedAccessTablePermissions.Update
| SharedAccessTablePermissions.Delete
};
string sasToken = table.GetSharedAccessSignature(
policy, null, null, null, null, null);
This returns the query portion of the URL you will need to create a SAS URL.
Another, code free way, to get a SAS URL is to go to https://azure.microsoft.com/en-us/features/storage-explorer/ and download the Azure Storage Explorer. When you start it up it will show you the "Connect to Azure Storage" dialog. In that case:
Select "Use a connection string or a shared access signature URI" and click next
Select "Use a connection string" and paste in your connection string from the Azure Portal for your Azure Cosmos DB Table API account and click Next and then click Connect in the next dialog
In the Explorer pane on the left look for your account under "Storage Accounts" (NOT Cosmos DB Accounts (Preview)) and then click on Tables and then right click on the specific table you want to explore. In the right click dialog you will see an entry for "Get Shared Access Signature", click on that.
A new dialog titled "Generate Shared Access Signature" will show up. Unfortunately so will an error dialog complaining about "NotImplemented", you can ignore that. Just click OK on the error dialog.
Now you can choose how to configure your SAS, I usually just take the defaults since that gives the widest access permission. Now click Create.
The result will be a dialog with both a complete URL and a query string.
So now we can take that URL (or create it ourselves using the query output from the code) and create a fiddler request:
GET https://tableaccount.table.cosmosdb.azure.com/ATable?se=2018-01-12T05%3A22%3A00Z&sp=raud&sv=2017-04-17&tn=atable&sig=X&$filter=PartitionKey%20eq%20'Foo'%20and%20RowKey%20eq%20'bar' HTTP/1.1
User-Agent: Fiddler
Host: tableaccount.table.cosmosdb.azure.com
Accept: application/json;odata=nometadata
DataServiceVersion: 3.0
To make the request more interesting I added a $filter operation. This is an OData filter that lets us explore the content. Note, btw, to make filter work both the Accept and DataServiceVersion headers are needed. But you can use the base URL (e.g. without the filter parameter) to make any of the REST API calls on a specific table.
Do be aware that the SAS token is scoped to an individual table. So higher level operations won't work with this SAS token.
I have an API that includes an account ID as part of the url (e.g. /account/7319310/report) where 7319310 is then account ID.
There are different credentials for each account, stored in MySQL although they could be stored in another manner if it made it easier.
I'd like Paw to automatically use the correct credentials based on the account parameter in the URI (it's always the second element) - is this possible?
In paw you can use a regex Dynamic to extract the data you need from the url:
Paw does not have a direct connection to MySQL, you can make http request from a custom value but you would need a server running to push these request to the server. A better option would be to save the credentials into a flat json file.
{
"1234334": {
"key1": 123456,
"key2": 345211
}
}
With this saved you can load this json file in a Custom Dynamic Value:
Here you can embed the extracted user id by using the regex dynamic value. inline in the code. Paw will reload the file on every request so you could set up a cron job to dump your database to this JSON file.
I am working with testing out provisioning for an embedded device where I can't save the API key and feed ID when power cycling.
After activating the product once, I get 403 forbidden when trying to fetch the device API key and feed id for the second time, even though I am supplying a master API key (with read permission) when making the request. The request works however when using API key belonging to the device, which is an inadequate solution considering I don't have access to that API key.
My GET request is formatted as follows:
GET /v2/devices/<activation code>/activate.csv HTTP/1.1
Host: api.xively.com
X-ApiKey: <master API key>
Content-Length: 0
So, is there a way for an already activated device to receive its API key and feed ID?
A device can only be activated once. However, if you have a master key, retrieving the devices API key should be pretty easy. You say you don't know the devices feed ID, but if you used the activation endpoint I imagine you know its serial number?
If you do know its serial number try making a GET request to https://api.cosm.com/v2/products/PRODUCT_ID/devices/DEVICE_SERIAL with your master API key in the X-ApiKey header.
This should return the following JSON, with you feed ID and API key.
{
"device": {
"serial": "SERIAL",
"activation_code": "ACTIVATION_CODE",
"created_at": "2013-05-05T18:11:42Z",
"activated_at": "2013-10-18T16:25:07Z",
"feed_id": FEED_ID,
"api_key": "DEVICE_API_KEY"
}
}
You should also be able to make consecutive activations, if you pass the API key you got from the first activation.
I created a new app a week ago and am trying to access the soap API however using the endpoint address as specified in the documentation (https://developer.gracenote.com/sites/default/files/web/html/index.html#PDFs/Music Web API Release Notes (1.1)-o.pdf) will not resolve the URL.
I am connecting to the end point https://cxxxxxxxx.web.cddb.net/webapi/xml/1.0 (I am replacing the xxxxxxxx with my client id)
If I run an nslookup off cxxxxxxxx.web.cddb.net from Googles public servers 8.8.8.8 I get non-existent domain. (Again I am replacing the xxxxxxxx with my client id)
Does the free license not work for the web API?
Thanks for the response
Looks like you are missing one letter! From the documentation on https://developer.gracenote.com/web-api, the correct endpoint should be:
https://cXXXXXXX.web.cddbp.net/webapi/xml/1.0/
Notice the "p" at the end of the "cddb".
Also, make sure that you replace the XXXXXX's with only the digits that precede the hyphen in your Client ID string. i.e. if your Client ID string is "1234567-09876543210987654321" (not a real Client ID), then you would send requests to https://c1234567.web.cddbp.net/webapi/xml/1.0/