How to make API calls from static nuxt page (clientside) without exposing api-token

Hi there,
I am using Nuxt.js and would like to consume my awesome Cockpit API from a static Nuxt app only (nuxt generate). This means there is no server at all and my master token is exposed on client-side so that all my API endpoints are exposed too.

Is there a way to resolve this using client-side requests only? Can I get a token as response from the API after logging in as admin without a token???

Cheers

Firstly, you should not use the master key in your app, create api keys that are used only for the endpoints you need.
Secondly, it depends on what you type of operations you are doing, you are just consuming data? Or you are saving data into Cockpit? If you are just fetching data, exposing the api key that is responsible for those operations is not a big issue, you may take other type of measures (e.g. cors, webserver configurations, etc…).

1 Like

First of all thank you for the support!

Sorry for my inaccurate explanation/usage of “consume”. Actually, I would like to save data into Cockpit. How to securely handle this? Is CORS enough for that?

Well, CORS is not enough, as someone can spoof the origin easily. This is something more related to API security than Cockpit himself and also the purpose of your application (e.g. requires user authentication), you need to implement multiple measures, like said, CORS, set a rate limit for requests to avoid DoS attacks, enforce HTTPS always, use an API gateway…

I decided to use guzzle for building a minimalistic api gateway (can I call it like this?). Whenever I post something to cockpit it will first go through my gateway.php file and do the relevant measures without exposing tokens.

But now my question is: How can I setup a simple user authentication with Cockpit? Or should I use the gateway in combination with some token library e.g. https://jwt.io/?

Thanks

Well, I would rely on a 3rd party for authentication (e.g. auth0, netlify identity, etc…), but that is still possible only with cockpit api, check as a reference that addon - https://github.com/agentejo/Auth0, is specific for auth0, but you can understand from the code how you can build your own

Hello again,
I do not understand what the auth0-addon does. Maybe you could give me a short clarification?

So far I get this:
My Nuxt app retrieves a token from auth0 after a successful login with those user credentials setup on auth0. The user’s meta has a reference to cockpit user group “admin”. From here I am stuck, because I do not understand how cockpit should know that the user is logged in. To be honest: I do not understand the connection between cockpit and auth0 service at all.

Edit: The most confusing line of code is:

//   bootstrap.php line 18
$ch = curl_init('https://agentejo.eu.auth0.com/userinfo');

Cheers

I mentioned the addon just as an example for dealing with authentication in cockpit using a third party, however, it seems a bit outdated (the oauth lock library needs to be updated (to 11.x) and there are some hardcoded values like the line you mentioned (needs to be replaced with value form config), besides that it should work). Resuming, assuming that the addon was working properly, it would completely replace the authentication on cockpit with auth0 service and that is only relevant if you don’t use cockpit default authentication.

If you are handling auth0 with nuxt or other js framework, you can perform the authentication entirely without cockpit, if you need to access the user from cockpit (e.g. when posting data to cockpit via api), you can pass the auth0 accessToken in your requests to cockpit and validate the accessToken there and retrieve any user details.

[…] and that is only relevant if you don’t use cockpit default authentication.

Do I need to disable Cockpit default authentication somewhere or do I need to add anything else to config/config.yaml besides given config params to activate auth0 addon? Because until now the auth0 addon does not replace anything and the login stays default. No error messages are given (btw. how can I debug in Cockpit?) and no trace of the addon is visible anywhere.

Thank you for your clarifications until now :slightly_smiling_face:

yeah, you need something like:

auth0:
  enabled: true
  id: xxxxx
  domain: xxxxxx.eu.auth0.com
  secret: xxx_xxxxxx_xxxx
  database: Username-Password-Authentication

but as said before the addon seems to have some hardcoded values that you may need to change, better to make those questions in the addon repo to the author

I had the same issue like this, but I am not sure how secure this method is. But it works well for me.

Refer to aheinze’s answer here

You can create the following file " /config/api/public/auth.php " with the following code:

   <?php

   return  $this->invoke('Cockpit\\Controller\\RestApi', 'authUser');

And you can query " /api/public/auth " without a token :point_up:

Here, when user log in with “username & password” and send a request to /config/api/public/auth.php :point_up:, it will return the token key like JWT scenario. Then, you can save it in LocalStorage.

To protect the specific routes or permissions of each keys,
I use this Group addon.

1 Like

Thank you for this solution.
From a security point of view, one should know that this would be a static token without expiration. A worst case scenario could be a third party trying to sniff the token from the user’s local storage and accessing the api. The mentioned Group addon would protect specific routes, but not those which the application has to use via the user’s token.

1 Like

@ekntrtmz
I’m glad it helped.

Client security like (LocalStorage or Cookie) is not really reliable, it’s better to protect from the server and domain too.


For me, Cockpit token key is like a virtual key. :smiley:

It can easily be replaced or renewed like the following example.

fetch('/api/cockpit/saveUser?token=old_token_key', {
    method: 'post',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
        user: {
           "_id":"5c7d54e331396101280003d1", // User's ID
           "api_key":"new_token_key" // New token..
           } 
    })
})
.then(user => user.json())
.then(user => console.log(user));

(Probably we can set a time limit for every key like JWT. :smile:)


I don’t really get what you said “which the application has to use via the user’s token”

but this is how I use group addon in my project.
I create a new user group called “guest” and give them “read only” permission.
So, whenever they login and send request with their keys, they can only read the collections,singletons & forms.

I added a little something that auto-refreshes the users api_key on every auth

# /config/api/public/auth.php
<?php

$this->request->request['generateApiKey'] = true;

return  $this->invoke('Cockpit\\Controller\\RestApi', 'authUser');

Returns the $user object with the new api_key.

And based on that Idea I created myself a /token endpoint to retrieve a fresh token from time to time

# /config/api/token.php
<?php

$user = $this->module('cockpit')->getUser();

if(!$user){
    $this->response->mime = 'json';
    $this->stop(403);
}

$this->request->request['apiKey'] = $user['api_key'];
$this->invoke('Cockpit\\Controller\\RestApi', 'refreshUserApiKey');

$user = $this->storage->findOne('cockpit/accounts', ['_id' => $user['_id']]);

return [
    'api_key' => $user['api_key']
];
1 Like