Getting CloudKit CKAsset URL? - ios

I'd like to share a CloudKit asset in the public database with people who aren't using my app. From the CKAsset class, there's only a fileURL property, which points to a local file, so no URL given.
On the documentation for CloudKit Web Services, however, there is a "referencing existing assets" request, which in its' response gives a downloadURL to the asset file.
Calling this from my app gives me a 421 status code, which is described in documentation as "AUTHENTICATION_REQUIRED", as it requires a ckSession parameter to be given, which is a "session identifier of an authenticated user". It also passes a redirectURL, where a user can type in their iCloud login, and then be re-directed back to the web application, now with a session identifier.
Opening this URL inside the app didn't immediately return the session identifier, but instead expected the user to go through the login process in a browser, before receiving their session ID.
This seems totally off-track, and the wrong thing to be doing. I'm certainly not going to make my users log in to iCloud through a web interface. The only thing missing for that CloudKit Web Services url is the session ID. Is there a way for me to get that using the CloudKit framework? Or is there another way for me to find out this file's URL?

If you are using Apple's official CloudKit.js, here is the code to configure your CloudKit access:
window.addEventListener('cloudkitloaded', function() {
console.log('Cloudkit loaded');
CloudKit.configure({
locale: 'en-us',
containers: [{
containerIdentifier: 'iCloud.com.getYoursInXcode',
apiTokenAuth: {
apiToken: 'getThisInCloudkitDashboardApiAccess',
persist: true //Set cookie
},
environment: 'production'
}]
});
// Do your thing here
})
If you are fetching records in your backend, it is still better to get your API token instead of using the Server-To-Server process.
For example, query records in GO, to fetch a record, compose your request body like this
sampleRequest = []byte('{
"resultsLimit":"4",
"desiredKeys": ["recordTitle","recordDescription"],
"query": {
"recordType": "myRecord",
"sortBy": {
"fieldName": "rank",
"ascending": true
}
}
}')
And send it to the following Url
http.NewRequest("POST", ""https://api.apple-cloudkit.com/database/1/iCloud.com.yourIdentifier/production/public/records/query?ckAPIToken=YOURTOKEN", bytes.NewBuffer(RequestBody))

Related

Can login or register only with Firefox using Sanctum API authentication (CSRF token mismatch)

I am developing an SPA with Laravel 9, Vuejs 3 and Sanctum. I am newbie to vue and to Sanctum and I use the sanctum API authentication instead of the token authentication.
At this stage I am in dev and run the embedded laravel server for laravel app and vite server for SPA.
Everything is going smoothly when I sign in and out using the Firefox browser. But when I use Google Chrome or other browser based upon chrome (Brave, Vivaldi, chromium) I cannot sign in nor register. I get a CSRF token mismatch response.
Here are my login an register methods from vuex 's store
actions: {
async register({ commit }, form) {
console.log("in register of index");
await axiosClient.get("/sanctum/csrf-cookie");
return axiosClient.post("/api/register", form).then(({ data }) => {
console.log("data dans index");
console.log(data);
return data;
});
},
async login({ commit }, user) {
await axiosClient.get("/sanctum/csrf-cookie");
return axiosClient
.post("/api/login", user)
.then(({ data }) => {
commit("SET_USER", data);
commit("SET_AUTHENTICATED", true);
//commit("setAuth", true);
return data;
})
.catch(({ response: { data } }) => {
commit("SET_USER", {});
commit("SET_AUTHENTICATED", false);
});
},
Could somebody help me making out what is wrong or missing?
Edited after Suben's response
I read from somebody that the trouble in Chrome could come from the domain being localhost instead of http://localhost in sanctum config.
Thus I did that and could manage to login with both browser. The trouble is that even with a satisfactory answer to login and the reception of the csrf-token now in both browser the store state is not set despite the answer in the .then function being a valid user object.
Moreover, doing 3 similar requests after that strange situation, the 3 of them being under the auth:sanctum middleware, the first failed with csrf-token mismatch, the second succeeded and the third failed also with csrf-token mismatch. Looking at the requests, they have exactly the same 3 cookies including one with the csrf-token.
My guess is, that RESTful APIs are stateless. That means, they do not worry about sessions. https://restfulapi.net/statelessness/
As per the REST (REpresentational “State” Transfer) architecture, the server does not store any state about the client session on the server-side. This restriction is called Statelessness.
When you login a user with Laravel's SPA authentication, then you ARE storing client session data on the server-side.
So you have two options:
You are moving the endpoint /api/login to web.php (logout too!) OR...
You are using the API token based login.
EDIT:
I had my problems at first too with Laravel Sanctums SPA authentication and Vue. There is a video, which goes through a lot of cases, that might help you aswell for the future (Configuration of cors.php and more): https://www.youtube.com/watch?v=It2by1dL50I

Azure AD Ms Identity callback URL (error AADSTS50011)

I'm integrating Azure AD and MS-Identity on a web app with Angular.
It works on my machine, but when I deploy it, I get an issue with the callback URL.
First, to make sure the callback URL is ok, I extract it from the microsoft login popup window's URL:
Then, I url decode the content. The URL seems fine and it is available in my Azure app's redirect URL.
Then I login to Microsoft normally and I get this error (AADSTS50011):
Then I inspect the URL again (inside the query string from the urldecoded popup window's URL) and now the URL seems to have been "tampered with".
It's now something like this:
http://somedomain:80/some_page/somequerystring
instead of
https://somedomain/some_page/somequerystring
so I wonder if it's part of the problem or if it's normal behavior.
It is also mentionned "If you contact your administrator, send this info to them." I suppose I'm the "administrator" so what can I do with that "Copy info to clipboard" info to investigate the problem?
Is your application hosting on http (80) or https (443)? If your app service is terminating your TLS connection and handling that for you instead of your app, your sign-on will construct the redirect using the http request scheme. I hooked into the OnRedirectToIdentityProvider event to correct the scheme.
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(options =>
{
Configuration.Bind("AzureAd", options);
options.Events ??= new OpenIdConnectEvents();
options.Events.OnRedirectToIdentityProvider += _fixRedirect;
});
...
private async Task _fixRedirect(RedirectContext context)
{
context.Request.Scheme = "https";
if(!context.ProtocolMessage.RedirectUri.StartsWith("https"))
context.ProtocolMessage.RedirectUri =
context.ProtocolMessage.RedirectUri.Replace("http", "https");
await Task.CompletedTask;
}

Is there a way to link the Google Identity Token to my company user in the database?

I'm setting up an action on google project which uses the OAuth & Google Sign In Linking Type.
Previously, I was using the userId that was sent in every request to look up the user in the database to see if there were accesstokens and refreshtokens available. But since userId is deprecated, I am looking for an alternative.
The user starts his/her dialog and then bumps into this piece of code:
app.intent('Give Color', async (conv, { color }) => {
conv.data[Fields.COLOR] = color;
if (conv.user.ref) {
await conv.user.ref.set({ [Fields.COLOR]: color });
conv.close(`I got ${color} as your favorite color.`);
return conv.close('Since you are signed in, I\'ll remember it next time.');
}
return conv.ask(new SignIn(`To save ${color} as your favorite color for next time`));
});
The "To continue, link Test App to your Google Account" on which the user selects the correct Google account.Then my /token endpoint is called on the OAuth server containing the Google ID Token (assertion) which holds all of the users data. I decode it, check in the database if the "sub" is already present, and I throw the following exception:
return res.status(401).send({ error: 'user_not_found' });
Then the normal OAuth procedure kicks in, where I deliver a token to Google. Sidenote: this is my own OAuth Server written in NodeJS. I am sure that the access- and refreshtoken are delivered to Google.
After token delivery, I get a new request on my action:
app.intent('Get Sign In', async (conv, params, signin) => {
if (signin.status !== 'OK') {
return conv.close('Let\'s try again next time.');
}
const color = conv.data[Fields.COLOR];
await conv.user.ref.set({ [Fields.COLOR]: color });
return conv.close(`I saved ${color} as your favorite color. `
+ 'Since you are signed in, I\'ll remember it next time.');
});
The signin.status has a value of "OK". But shouldn't the conv.user object contain the Google ID Token so that I can store the access- and refreshtoken along with this "sub" from the Google ID Token in my database? Or am I getting something wrong?
The content of the conv.user looks like this:
User {raw: Object, storage: Object, _id: undefined, locale: "en-BE", verification: "VERIFIED", …}
_id: undefined
[[StableObjectId]]: 7
access: Access {token: "ACCT-ATlbRmcpMI545WJFssRSlK1Jcza46NIB"}
entitlements: Array(0) []
id: undefined
last: Last {seen: Thu Aug 08 2019 10:53:17 GMT+0200 (Central Europea…}
locale: "en-BE"
name: Name {display: undefined, family: undefined, given: undefined}
permissions: Array(0) []
profile: Profile {token: undefined}
raw: Object {accessToken: "ACCT-ATlbRmcpMI545WJFssRSlK1Jcza46NIB", locale: "en-BE", lastSeen: "2019-08-08T08:53:17Z", …}
storage: Object {}
verification: "VERIFIED"
__proto__: Object {constructor: , _serialize: , _verifyProfile: , …}
conv.user.id is *DEPRECATED*: Use conv.user.storage to store data instead
It won't contain the Google ID of the user, because the user hasn't authorized that.
What they have authorized is whatever you've asked them to authorize via your OAuth server.
So you'll see the access token that your server has sent to the Assistant in conv.user.access, and you can then use this token to lookup who the user is in your database and take action accordingly.
If you specifically want their Google ID, you'll need to make sure that they use Google Sign-In on the same project as your Action (either through voice, a mobile app, or a webapp).
If you just need an ID so you can see when this user returns later, you can use the Google ID you get from Google Sign-In, or just generate an ID and store this in conv.user.storage.
Since I just want to have an ID, I will be using this:
If you just need an ID so you can see when this user returns later, you can use the Google ID you get from Google Sign-In, or just generate an ID and store this in conv.user.storage.
Thanks!

Google embedded assistant service gives an error about text_config missing when it is included

I'm trying to use the embedded Google Assistant service (https://developers.google.com/assistant/sdk/reference/rpc/google.assistant.embedded.v1alpha2) to send some text queries from an app I'm writing in Rust. As there's no official client library and I haven't been able to get either of the GRPC libraries I've tried working, I'm POSTing an AssistRequest proto directly to https://embeddedassistant.googleapis.com/$rpc/google.assistant.embedded.v1alpha2.EmbeddedAssistant/Assist. After getting the appropriate OAuth token and registering a device ID, I send a proto like this:
config {
text_query: "what's the time"
audio_out_config {
encoding: MP3
sample_rate_hertz: 16000
}
screen_out_config {
screen_mode: PLAYING
}
dialog_state_in {
language_code: "en-US"
}
device_config {
device_id: "my_device_id"
device_model_id: "<my model id>"
}
}
(with a valid device_id and device_model_id filled in). However, I always get a 400 back, with the error
"Invalid \'AssistConfig\': audio_in_config or text_query must be provided."
Why am I getting this error, when text_query is clearly filled in? I get the same error if I try setting audio_in_config.
Answering my own question, it turned out I needed to wrap the request in a StreamBody proto, putting the serialised AssistRequest in the message field. This was not documented anywhere I could see though!

Shared authentication between rails and node.js with redis store

I have a rails app and a node.js app and I use Devise to authenticate users. I store the session with Redis. Now I'd like that when a user go to the node app, the app checks through socket.io whether the user is logged in or not. I managed to get the session datas from redis but I don't know how to interpret them to check if the user is logged in.
Here is my code for the node app which checks if the _session_id exists in the database and retrieves the session datas:
io.set('authorization', function (data, accept) {
if (data.headers.cookie) {
data.cookie = cookie.parse(data.headers.cookie);
data.sessionID = data.cookie['_session_id'];
redis.get(data.sessionID, function (err, session) {
if (err || !session) {
accept('Error', false);
} else {
data.session = session;
console.log(session);
accept(null, true);
}
});
} else {
return accept('No cookie transmitted.', false);
}
});
This is what the console.log(session) gives me:
{I"_csrf_token:EFI"1HPglfkCCagvb1LLraU1CEEyx7AtDzztqAEPY5G5lNgY=;FI"warden.user.user.key;TI" User;F[iI""$2a$10$IHq2WAhwbaqR4WWajRE/Yu;T
How can I check if a user is logged in the rails app with the node app?
Thanks
EDIT: It appears that the redis store gem I use calls a Marshalling method before storing the session in database. So I bypassed the problem by overriding the Marshalling method and stored the session datas in JSON format. It's not very elegant so if you find a better way to share sessions between rails and node.js, please let me know.
It might be easier to create your own oauth api(there's a railscast on how to do this oauth). As far as I know, devise is a ruby gem and isn't really cross-platform but oauth can be used in almost any language. You can add an oauth token to devise which should allow you to pass that token to node.js.
You can easily do it doing few tweaks to the index.js method in Rails-Cookie-Parser for Express https://github.com/instore/rails-cookie-parser library to use it without Express.
This library uses Marshal npm as its dependency
Note: You might need to decodeURIComponent("cookie value") since the original cookie is URL encoded

Resources