Ga naar hoofdinhoud

Wachtwoorden: wachtwoord wijzigen

Met registreren en inloggen heb je de basis, maar een gebruiker moet zijn wachtwoord ook kunnen veranderen.

Het oude wachtwoord eerst​

Een nieuw wachtwoord zet je pas in de database als het oude klopt. Je server heeft in deze reeks geen sessie: wie alleen sara meestuurt, zou anders het wachtwoord van Sara veranderen en haar buitensluiten. Het begin is daarom hetzelfde als bij /inloggen:

@app.post("/wijzig")
async def wijzig(naam: str = Form(...), oud: str = Form(...), nieuw: str = Form(...)):
with SqliteDict("gebruikers.db") as db:
opgeslagen = db.get(naam, NEP)
try:
ph.verify(opgeslagen, oud)
except VerifyMismatchError:
return {"bericht": "Naam of wachtwoord klopt niet"}
if opgeslagen == NEP:
return {"bericht": "Naam of wachtwoord klopt niet"}
with SqliteDict("gebruikers.db") as db:
db[naam] = ph.hash(nieuw)
db.commit()
return {"bericht": "Wachtwoord gewijzigd"}
  1. Regel 3-10:

    Net als bij inloggen: opzoeken met de nep-hash als reserve, ph.verify met het oude wachtwoord, en dezelfde melding bij een onbekende naam.

  2. Regel 11-13:

    Pas als het oude wachtwoord klopt, open je de database opnieuw en bewaar je de hash van het nieuwe.

Zet dit endpoint in hetzelfde main.py als /registreer en /inloggen: het gebruikt dezelfde imports, dezelfde ph en dezelfde NEP. Zonder de nep-hash zou ook /wijzig met zijn snelle antwoord verraden welke namen bestaan.

Wijzigen testen​

Maak een scriptje wijzig.py. Het probeert het wachtwoord van Sara eerst te wijzigen met een fout oud wachtwoord, en daarna met het goede. Daarna logt het in met het oude en met het nieuwe wachtwoord:

import httpx

adres = "http://127.0.0.1:8000"

wijzig = {"naam": "sara", "oud": "fout", "nieuw": "Lange-Nieuwe-Zin-7"}
print(httpx.post(adres + "/wijzig", data=wijzig).json())
wijzig["oud"] = "welkom123"
print(httpx.post(adres + "/wijzig", data=wijzig).json())

for wachtwoord in ["welkom123", "Lange-Nieuwe-Zin-7"]:
inloggen = {"naam": "sara", "wachtwoord": wachtwoord}
print(httpx.post(adres + "/inloggen", data=inloggen).json())
Zo ziet je main.py er nu uit
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
from fastapi import FastAPI, Form, HTTPException
from sqlitedict import SqliteDict

app = FastAPI()
ph = PasswordHasher()
NEP = ph.hash("niemand")

@app.post("/registreer")
async def registreer(naam: str = Form(...), wachtwoord: str = Form(...)):
with SqliteDict("gebruikers.db") as db:
if naam in db:
raise HTTPException(status_code=400, detail="Deze naam is al bezet")
db[naam] = ph.hash(wachtwoord)
db.commit()
return {"bericht": f"Welkom, {naam}"}

@app.post("/inloggen")
async def inloggen(naam: str = Form(...), wachtwoord: str = Form(...)):
with SqliteDict("gebruikers.db") as db:
opgeslagen = db.get(naam, NEP)
try:
ph.verify(opgeslagen, wachtwoord)
except VerifyMismatchError:
return {"bericht": "Naam of wachtwoord klopt niet"}
if opgeslagen == NEP:
return {"bericht": "Naam of wachtwoord klopt niet"}
return {"bericht": "Ingelogd"}

@app.post("/wijzig")
async def wijzig(naam: str = Form(...), oud: str = Form(...), nieuw: str = Form(...)):
with SqliteDict("gebruikers.db") as db:
opgeslagen = db.get(naam, NEP)
try:
ph.verify(opgeslagen, oud)
except VerifyMismatchError:
return {"bericht": "Naam of wachtwoord klopt niet"}
if opgeslagen == NEP:
return {"bericht": "Naam of wachtwoord klopt niet"}
with SqliteDict("gebruikers.db") as db:
db[naam] = ph.hash(nieuw)
db.commit()
return {"bericht": "Wachtwoord gewijzigd"}

Registreren, inloggen en wijzigen in één server. Een hash met oude instellingen blijft nog zoals hij is; dat verandert in les 10. En iedereen kan nog eindeloos wachtwoorden proberen; daar komt in les 11 een limiet op.

Controlevraag
Waarom vraagt /wijzig om het oude wachtwoord, terwijl de naam toch zegt om wie het gaat?
Kies wat jij denkt. Bij het goede antwoord zie je de uitleg.

Er gaat iets mis​

Wijzigen lukt de tweede keer niet​

Je draait wijzig.py nog een keer, en krijgt:

{'bericht': 'Naam of wachtwoord klopt niet'}
{'bericht': 'Naam of wachtwoord klopt niet'}
{'bericht': 'Naam of wachtwoord klopt niet'}
{'bericht': 'Ingelogd'}

Oorzaak: de eerste keer heeft het script het wachtwoord van Sara al veranderd. welkom123 is niet meer haar oude wachtwoord, dus /wijzig weigert. Dit is je controle die werkt.

Oplossing: niets, Sara heeft nu Lange-Nieuwe-Zin-7. Wil je de test opnieuw doen, gooi dan gebruikers.db weg en draai registreer.py en wijzig.py opnieuw.

Opdrachten​

Opdracht 1: Predict - Hetzelfde wachtwoord opnieuw​

Sara wijzigt haar wachtwoord nog een keer, en typt bij nieuw precies hetzelfde als bij oud: Lange-Nieuwe-Zin-7.

Vraag: staat er daarna dezelfde code bij sara in de database als ervoor? En kan ze nog inloggen met Lange-Nieuwe-Zin-7?

Tip

Wat doet ph.hash elke keer anders? Bekijk het daarna zelf met lees_database.py, voor en na het wijzigen.

Antwoord

Een andere code, want ph.hash maakt bij elke hash een nieuw zout. Inloggen met Lange-Nieuwe-Zin-7 lukt nog steeds: ph.verify haalt het nieuwe zout uit de nieuwe hash.

Door naar les 10: een oude hash bijwerken.