snipe / snipe-it

A free open source IT asset/license management system
https://snipeitapp.com
GNU Affero General Public License v3.0
10.84k stars 3.13k forks source link

API Key Request #10648

Open MaKaNu opened 2 years ago

MaKaNu commented 2 years ago

Snipe-IT Version

5.3.8

Operating System

debian

Web Server

Apache2

PHP Version

7.4

Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

If I want to use the API to create a qt Application for smartphones and desktops, I need to explain everybody how to insert the API Key or Share the Application as Administrator with installed API Key.

Describe the solution you'd like A clear and concise description of what you want to happen.

So I could Instead let the user login into the app with the same credentials as to the webserver. The Application will than makes a POST request to create an API Key which will be used in the active Session of the Application. I know this procedure and also already implemented on postgrest but have no idea about php.

Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

No response

Additional context Add any other context or screenshots about the feature request here.

No response

welcome[bot] commented 2 years ago

👋 Thanks for opening your first issue here! If you're reporting a 🐞 bug, please make sure you include steps to reproduce it. We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can.

snipe commented 2 years ago

I honestly don't know what you're asking here. Postgres is a database, PHP is a scripting language, so I don't know what the ask is.

API keys are tied to the user who created them. It sounds like maybe you're trying to build an Android app? If so, I'd suggest using SAML/OAuth, or requiring the user's API key. If they don't have permission to create an API key, that's a deliberate c choice by the admin who created their user account.

MaKaNu commented 2 years ago

To clearify: I adminstrate a self hosted snipe-it server. So creating Userkeys and give rights to create keys to my users is not the issue. You missunderstood the "t" in postgrest maybe as a typo, which is not a typo. postgrest is a tool written in haskell, which transfers a postgres database into a restfull api. Actually before switching to Snipe-it we planned to use postgrest with postgres for a inventory database but were pleased by the snipe-it webserver.

So, what I was talking about is described in this chapter of the postgrest documentation. Based on a created API Key, your system and postgrest using both jwt as the bearer token.

postgrest describes two possible solutions: Using OAuth for jwt token creation or building database functions yourself. We tested postgrest so far and were able to do this. Since at the moment OAuth is not an option for us (public domain) only two option are to create the keys via Webserver or artisian. So since not all my users are that capable of working with API Keys, I was looking for a solution to maybe call the php artisan passport:client --personal via the REST API.

snipe commented 2 years ago

Ah, I see - thanks for the clarification.

IIRC, once you have created a password grant client, you may request an access token by issuing a POST request to the /oauth/token route with the user's email address and password.

snipe commented 2 years ago

These are the API token related routes we currently have:

 php artisan route:list | grep passport
|        | GET|HEAD      | oauth/authorize                                       | passport.authorizations.authorize       | Laravel\Passport\Http\Controllers\AuthorizationController@authorize        | web,auth                                             |
|        | POST          | oauth/authorize                                       | passport.authorizations.approve         | Laravel\Passport\Http\Controllers\ApproveAuthorizationController@approve   | web,auth                                             |
|        | DELETE        | oauth/authorize                                       | passport.authorizations.deny            | Laravel\Passport\Http\Controllers\DenyAuthorizationController@deny         | web,auth                                             |
|        | GET|HEAD      | oauth/clients                                         | passport.clients.index                  | Laravel\Passport\Http\Controllers\ClientController@forUser                 | web,auth                                             |
|        | POST          | oauth/clients                                         | passport.clients.store                  | Laravel\Passport\Http\Controllers\ClientController@store                   | web,auth                                             |
|        | PUT           | oauth/clients/{client_id}                             | passport.clients.update                 | Laravel\Passport\Http\Controllers\ClientController@update                  | web,auth                                             |
|        | DELETE        | oauth/clients/{client_id}                             | passport.clients.destroy                | Laravel\Passport\Http\Controllers\ClientController@destroy                 | web,auth                                             |
|        | GET|HEAD      | oauth/personal-access-tokens                          | passport.personal.tokens.index          | Laravel\Passport\Http\Controllers\PersonalAccessTokenController@forUser    | web,auth                                             |
|        | POST          | oauth/personal-access-tokens                          | passport.personal.tokens.store          | Laravel\Passport\Http\Controllers\PersonalAccessTokenController@store      | web,auth                                             |
|        | DELETE        | oauth/personal-access-tokens/{token_id}               | passport.personal.tokens.destroy        | Laravel\Passport\Http\Controllers\PersonalAccessTokenController@destroy    | web,auth                                             |
|        | GET|HEAD      | oauth/scopes                                          | passport.scopes.index                   | Laravel\Passport\Http\Controllers\ScopeController@all                      | web,auth                                             |
|        | POST          | oauth/token                                           | passport.token                          | Laravel\Passport\Http\Controllers\AccessTokenController@issueToken         | throttle                                             |
|        | POST          | oauth/token/refresh                                   | passport.token.refresh                  | Laravel\Passport\Http\Controllers\TransientTokenController@refresh         | web,auth                                             |
|        | GET|HEAD      | oauth/tokens                                          | passport.tokens.index                   | Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@forUser  | web,auth                                             |
|        | DELETE        | oauth/tokens/{token_id}                               | passport.tokens.destroy                 | Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@destroy  | web,auth                                             |
MaKaNu commented 2 years ago

I tested a simple POST with login and passwort in the body:

POST http://our-snipe-it.de/api/v1/oauth/token/ HTTP/1.1

{ "email": "dbadmin", "password": "<dbadmin-password>" }

And got following as response:

HTTP/1.1 301 Moved Permanently
Date: Fri, 11 Feb 2022 14:00:22 GMT
Server: Apache/2.4.52 (Debian)
Location: http://our-snipe-it.de/api/v1/oauth/token
Content-Length: 348
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="our-snipe-it.de/api/v1/oauth/token">here</a>.</p>
<hr>
<address>Apache/2.4.52 (Debian) Server at our-snipe-it.de Port 80</address>
</body></html>

I am not sure but the Moved Permanently vanishes my hopes. I also got the same response if I POST without a payload.

snipe commented 2 years ago

It doesn't not appear that creating API tokens via the API is supported out of the box in Laravel's Passport.

✨snipe@chodeblossom✨ snipe-it  (develop) $ php artisan route:list | grep token
|        | POST          | oauth/personal-access-tokens                           | passport.personal.tokens.store          | Laravel\Passport\Http\Controllers\PersonalAccessTokenController@store           | web                                                  |
|        | GET|HEAD      | oauth/personal-access-tokens                           | passport.personal.tokens.index          | Laravel\Passport\Http\Controllers\PersonalAccessTokenController@forUser         | web                                                  |
|        | DELETE        | oauth/personal-access-tokens/{token_id}                | passport.personal.tokens.destroy        | Laravel\Passport\Http\Controllers\PersonalAccessTokenController@destroy         | web                                                  |
|        | POST          | oauth/token                                            | passport.token                          | Laravel\Passport\Http\Controllers\AccessTokenController@issueToken              | Illuminate\Routing\Middleware\ThrottleRequests       |
|        | POST          | oauth/token/refresh                                    | passport.token.refresh                  | Laravel\Passport\Http\Controllers\TransientTokenController@refresh              | web                                                  |
|        | GET|HEAD      | oauth/tokens                                           | passport.tokens.index                   | Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@forUser       | web                                                  |
|        | DELETE        | oauth/tokens/{token_id}                                | passport.tokens.destroy                 | Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@destroy       | web                                                  |
|        | GET|HEAD      | password/reset/{token}                                 | password.reset                          | App\Http\Controllers\Auth\ResetPasswordController@showResetForm                 | web

I don't see any API routes in there.

MaKaNu commented 2 years ago

I had recently some insights to oauth and how token creation with oauth works. Based on the insights it is clear why no API routes exist.

From my pov oauth is kind of the same as normal token creation, with the specific difference, that not the server to which the token will be created is responsible for token creation. Instead a rediraction to another trusted server (Google, Github, Outlook etc.) is used. The username and password will be entered to those servers (one of them), they create the token for the Server the token belongs to and authorizes the user.

This is handy if we only want to use oauth for login authorization, but can also be used for token creation inside a custom API program.

Conlusion:

  1. The feature request fits also with future implementation of oauth (not all users have access to a trusted server plattform for token creation or just don't want to use oauth at all.) and is not redundant
  2. After successful (or better while) implementation of oauth features for future releases, it might be a good strategy to have this feature in mind.
MaKaNu commented 1 year ago

I recently analyzed again the structure and figured out that a POST against oauth/personal-access-tokens with a Token-name in the payload is the correct attempt. A cURL request generated from firefox worked fine and I was able to reduce the request to the essentials including the two cookies: snipeit_session and XSRF-TOKEN. At that point I assume that a GET request against <snipit-url>/login followed by POST against the same URL will create those necessary TOKENS.

With firefox I recognized, that the POST included _token field at login. At the moment I am unable to create the correct value.