Ontwerp API login endpoint

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Carharttguy
  • Registratie: Juli 2010
  • Laatst online: 23-04 18:36
Hallo iedereen

Ik ben bezig met het ontwikkelen van een API. Nu heb ik niet zo veel ervaring, en probeer ik het zo "juist" mogelijk te doen.

Concreet heb ik nu enkele keuzes gemaakt:
  • Ik gebruik een soort van token authenticatie. Eerst moet de gebruiker ergens aanmelden met gebruikersnaam/wachtwoord en die krijgt dan een token terug. Die is tot x aantal minuten na de laatste request geldig
  • Ik gebruik zoveel mogelijk zelfstandige naamwoorden bij endpoints, geen werkwoorden
Concrete vraag: Ik heb een endpoint waar de gebruiken een token kan aanvragen. Dit is de /session endpoint.
Dit is een POST request, de gebruiker stuurt gebruikersnaam en wachtwoord door.

Nu zijn er volgens mij 3 manieren om dit te doen:
  1. Gebruiker geeft gebruikersnaam en wachtwoord door in JSON formaat in de body
  2. Gebruiker geeft de gebruikersnaam en wachtwoord door in de query parameters
  3. Gebruiker geeft de gebruikersnaam en wachtwoord door in de Authorization header, Basic style
Nu valt voor alles iets te zeggen, punt voor punt antwoord op bovenstaande lijst:
  1. Het is een POST, het moet in de body. Ok, maar waarom dan geen Bearer tokens etc in de body? Die worden altijd in de headers meegegeven, zelden in de JSON body
  2. Query parameters worden vaak gebruikt bij GET requests, waarom dan niet in de login endpoint? Er wordt strikt genomen geen nieuwe data aangemaakt (zoals wel het geval is bij bvb een POST naar de /users endpoint)
  3. In alle andere requests stuur ik een token mee in de authorization header, en dat doen veel API's. Maar vanaf iemand het woord Basic Authorization header zegt lijkt dit een groot taboe? Deze optie lijkt mij het meest HTTP correct, toch ben ik dit nog nooit tegengekomen 'in het wild'
Ik heb al heel veel zitten zoeken op goed ontwerp bij API, maar er is veel tegengestelde info te vinden.

Wat is jullie ervaring?

Acties:
  • +4 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 20-05 12:47
1 en 3 zijn beiden prima, waarbij 1 gewoon makkelijker is voor de client.
2 is een erg slecht idee. Echt, HEEL slecht. Requests worden namelijk over het algemeen gelogged en dan heb je dus username + PW in plaintext in je logfiles staan.

Je gebruikt NOOIT maar dan ook NOOIT privacy gevoelige informatie in URLs.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • DukeBox
  • Registratie: April 2000
  • Laatst online: 15:23

DukeBox

Voor je 't weet wist je 't nie

^ Dat en bijkomend nadeel van optie 2 is dat als er iets van een client proxy tussen zit je ook nog eens last kan krijgen van caching. Dat is ook waarom 1 mijn voorkeur heeft, omdat je altijd bij de juiste endpoint zit en betere controle op MITM.

Duct tape can't fix stupid, but it can muffle the sound.


Acties:
  • +1 Henk 'm!

  • thlst
  • Registratie: Januari 2016
  • Niet online
POST icm application/x-www-form-urlencoded kan ook, JSON hoeft niet

En waarom niet bij elke request gebruikersnaam/wachtwoord meegeven ipv een token? Dan is het stateless

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 20-05 12:47
thlst schreef op woensdag 12 mei 2021 @ 18:24:
En waarom niet bij elke request gebruikersnaam/wachtwoord meegeven ipv een token? Dan is het stateless
Een token is net zo stateless. Daarbij wil je liever niet aan de client kant een username/password in 't geheugen houden. Dan zou ik als jij even wegloopt van je computer heel simpel je wachtwoord plaintext kunnen achterhalen.

https://niels.nu


Acties:
  • +2 Henk 'm!

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 16:15

Haan

dotnetter

Ik zou denk ik optie 1 kiezen voor het verkrijgen van de token en daarna optie 3 voor alle volgende requests (dus de token in een header meesturen)

Maar heb je ook al gekeken naar standaarden zoals OAuth?

Kater? Eerst water, de rest komt later


Acties:
  • 0 Henk 'm!

  • M0nkeymen
  • Registratie: Maart 2009
  • Laatst online: 15:03

M0nkeymen

Monkeystyle!

Misschien Optie 4: laat het over aan een 3de partij, Bijv. Azure Ad b2c. Gebruikers loggen in bij hun, je krijgt terug in een token wat ze mogen. Dingen als 2FA zijn vaak ook wat makkelijker.

psn: M0nkeymen81 | Inglourious Guardians


Acties:
  • 0 Henk 'm!

  • Zebby
  • Registratie: Maart 2009
  • Laatst online: 15:07
Indien het echt een API is zou ik zelf werken met een stevige API key in plaats van username/wachtwoord. Ik weet niet of je nog verder een oplossing gebruikt, maar in tegenstelling tot een username/wachtwoord kun je met de API key alleen dat - de API gebruiken, niet nog inloggen op een ander stuk front-end/back-end, of gewoon weer een set credentials buit hebben gemaakt.
Beter 1 goede API key dan 2 matige maar verder te herleiden us/pw.

Nog beter is iets van OAuth, maar dat is imho wel wat meer complex.

Acties:
  • 0 Henk 'm!

  • Jurgle
  • Registratie: Februari 2003
  • Laatst online: 21-05 12:48

Jurgle

100% Compatible

Korte antwoord: er zijn allerlei richtlijnen, maar je mag helemaal zelf bepalen hoe je dit aanpakt.

That said, er zijn een paar 'best practices', 'de facto standaarden', of hoe je zoiets ook noemt.
Carharttguy schreef op woensdag 12 mei 2021 @ 17:03:
  1. Het is een POST, het moet in de body. Ok, maar waarom dan geen Bearer tokens etc in de body? Die worden altijd in de headers meegegeven, zelden in de JSON body
(Bij een POST kun je ook gewoon query parameters meegeven in de url)

De headers van een request/response schetsen de context, de body bevat de data van de entiteit waar het om gaat. In de headers kun je bijvoorbeeld zien welke talen iemand accepteert, of compressie ondersteund wordt, welke browser gebruikt wordt, hoe laat het request gedaan is, etc. De body is de entiteit (data).

Van RFC 2616
The message-body (if any) of an HTTP message is used to carry the entity-body associated with the request or response.
En je token is in deze context (over het algemeen) geen onderdeel van de entiteit, dus niet in de body. Maar, het is geen wet.
Carharttguy schreef op woensdag 12 mei 2021 @ 17:03:
  • Query parameters worden vaak gebruikt bij GET requests, waarom dan niet in de login endpoint? Er wordt strikt genomen geen nieuwe data aangemaakt (zoals wel het geval is bij bvb een POST naar de /users endpoint)
Prima, klopt. Kan ook gewoon. Wel is er hierboven al geschreven dat zaken die als parameter worden meegegeven als onderdeel van de url op plekken worden opgeslagen waar je geen credentials wil. Om caches en proxies even buiten beschouwing te laten wil je ook niet dat je credentials in een bookmark, je browse-geschiedenis of een e-mail geplakt worden.

(Met HTTPS zien proxies ook helemaal de url met parameters niet).

Overigens ben ik het er niet helemaal mee eens dat 'er geen nieuwe data aangemaakt wordt'; je maakt immers een 'sessie'-entiteit aan met een uniek ID: het token.

Wat ik opmerk is dat parameters als onderdeel van de (universal resource) identifier vaak worden gebruikt om eigenschappen van de data te veranderen, zoals een filter op een eigenschap van de entiteit(en), of de sorteervolgorde, of voor paginering, etc...
Carharttguy schreef op woensdag 12 mei 2021 @ 17:03:
  • In alle andere requests stuur ik een token mee in de authorization header, en dat doen veel API's. Maar vanaf iemand het woord Basic Authorization header zegt lijkt dit een groot taboe? Deze optie lijkt mij het meest HTTP correct, toch ben ik dit nog nooit tegengekomen 'in het wild'
Dat is ook helemaal prima. Over het algemeen wordt een token gebruikt om verantwoordelijkheden te scheiden: je API en een applicatie die gaat over identiteit. Zo hoeft jouw API bijvoorbeeld helemaal niet te weten wat iemands wachtwoord is, of dat er uberhaupt een wachtwoord nodig is. Misschien verkrijgt iemand toegang met een vingerafdruk.

Bovendien kun je aan een token eigenschappen koppelen die niet bij een username/wachtwoord-combinatie bestaan: een geldigheidsduur, overdraagbaarheid, anoniem (na verlopen weet je niet meer van wie het token was).

Basic Authentication is een methode bij HTTP waarin username en wachtwoord base64 encoded met een request wordt meegestuurd. Niets mis mee (over HTTPS) maar je verplicht hiermee je API om te gaan met username/wachtwoord.

My opinions may have changed but not the fact that I am right ― Ashleigh Brilliant


Acties:
  • +1 Henk 'm!

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 25-04 18:21
Zebby schreef op donderdag 13 mei 2021 @ 01:02:
Indien het echt een API is zou ik zelf werken met een stevige API key in plaats van username/wachtwoord. Ik weet niet of je nog verder een oplossing gebruikt, maar in tegenstelling tot een username/wachtwoord kun je met de API key alleen dat - de API gebruiken, niet nog inloggen op een ander stuk front-end/back-end, of gewoon weer een set credentials buit hebben gemaakt.
Beter 1 goede API key dan 2 matige maar verder te herleiden us/pw.

Nog beter is iets van OAuth, maar dat is imho wel wat meer complex.
API key of Username/PW... het zijn allebei credentials. Er is niks magisch aan een API dat ie alleen bij de API kan als je dat niet expliciet zo configureert.

Daarnaast, als je een API key buitmaakt kan je er al in. Maak je alleen een username of pw buit dan heb je die ander nog nodig. Daarnaast is herleiden van een pw het probleem niet.

En hoe definieer jij een stevige API key?

Engineering is like Tetris. Succes disappears and errors accumulate.


Acties:
  • +1 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 20-05 12:47
Jurgle schreef op donderdag 13 mei 2021 @ 01:19:
Prima, klopt. Kan ook gewoon.

[...]

Dat is ook helemaal prima.
Even een tip; als iets een heel slecht idee is, is het over het algemeen belangrijk om vooral bij beginners dit heel expliciet aan te geven. Alles "kan", maar jouw "kan gewoon" kan door TS geinterpreteerd worden alsof jij denkt dat dit een prima optie is.

https://niels.nu


Acties:
  • +1 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 20-05 12:47
Zebby schreef op donderdag 13 mei 2021 @ 01:02:
Indien het echt een API is zou ik zelf werken met een stevige API key in plaats van username/wachtwoord.
Je haalt een paar dingen compleet door elkaar. API keys worden meestal gebruikt voor toepassingen waarbij je de server die met jouw API praat wil kunnen identificeren. Dus stel ik maak een API voor crypto koersen en ik wil deze niet voor jan en alleman beschikbaar maken, dan laat ik developers zich registreren waarbij ze een API key krijgen. Voorbeeld hiervan is de OpenWeatherMap API.

Dit is overigens niet de enige optie voor server-server authenticatie. In niet publieke settings wordt vaak mutual TLS gebruikt.

Dat is niet waar de TS het over heeft. Hier gaat het om credentials voor eindgebruikers, dus waarbij er een individuele login gedaan wordt, en mensen een 'sessie' starten. Hoe je hier mee om gaat bij een API is niet anders dan hoe web pagina's werken met sessies. Iemand logged in via (bijv) een POST request met username en password, en de back-end geeft dan een token uit. Dit is gewoon een lange random string. Een session ID is eigenlijk gewoon exact hetzelfde als een API token. Het verschil zit hem er vooral in dat er meestal gebruik wordt gemaakt van expliciete tokens i.p.v. bijvoorbeeld een cookie geset wordt.

Je "stevige API key" is dus gewoon een API token, en zover was de TS al. Maar je ontkomt dus niet aan een username/password (even lost van OpenID e.d.) gebruiken want de gebruikers zullen toch in moeten kunnen loggen.

https://niels.nu


Acties:
  • 0 Henk 'm!

  • thlst
  • Registratie: Januari 2016
  • Niet online
Hydra schreef op woensdag 12 mei 2021 @ 19:11:
[...]


Een token is net zo stateless. Daarbij wil je liever niet aan de client kant een username/password in 't geheugen houden. Dan zou ik als jij even wegloopt van je computer heel simpel je wachtwoord plaintext kunnen achterhalen.
Als de API client backend-naar-backend is zie ik de meerwaarde van een token niet omdat de client toch altijd de user/pw moet hebben en gebruiken om een nieuw token aan te kunnen vragen na outage e.d.

Als de API client een SPA is voor eindgebruikers met Fikkie1990 als wachtwoord vind ik een token wel een goed idee.

Acties:
  • 0 Henk 'm!

  • Tom
  • Registratie: Juni 1999
  • Niet online

Tom

In mijn API geef ik een JWT-token uit op m'n login-endpoint en zet ik een httponly cookie (dat is belangrijk, want dan kun je er met JS niet bij, mocht een hacker het voor elkaar krijgen om scripts bij de client uit te voeren) met een sessie token. De JWT gebruik ik op mijn (beschermde) endpoints. Dat kunnen op die manier ook microservices zijn die niet bij je user table kunnen maar wel authenticatie nodig hebben.

De JWT verloopt geloof ik na 15-30 minuten. Dit wil je kort houden, mocht iemand een lang houdbare JWT te pakken krijgen dan kun je deze niet invalideren. Via een refresh-endpoint die naar het cookie kijkt krijg je weer een nieuwe JWT.

In de frontend vang ik een HTTP 401 af, mocht het response unauthorized zijn. Dan onthoud ik de oorspronkelijke call. Vervolgens gaat er een call naar de refresh-endpoint en krijg ik de nieuwe JWT. Vervolgens wordt de oorspronkelijke call nogmaals uitgevoerd. Zo wisselt de JWT zonder dat je het als gebruiker door hebt. Via bijvoorbeeld Axios kan dat met een interceptor.

Acties:
  • 0 Henk 'm!

  • Hydra
  • Registratie: September 2000
  • Laatst online: 20-05 12:47
Tom schreef op donderdag 13 mei 2021 @ 14:05:
In mijn API geef ik een JWT-token uit op m'n login-endpoint en zet ik een httponly cookie (dat is belangrijk, want dan kun je er met JS niet bij, mocht een hacker het voor elkaar krijgen om scripts bij de client uit te voeren) met een sessie token.
Even ter aanvulling; als dit geen omgeving is met veel (micro) services heeft een JWT niet perse meerwaarde t.o.v. gewoon een API token. Je haalt extra complexiteit naar binnen zonder dat je er mee wint.

Relevant linkje dat ik toevallig van de week las: https://evertpot.com/jwt-is-a-bad-default/

https://niels.nu

Pagina: 1