Hallitse FastAPI OAuth2 -todennusta! Tämä opas kattaa salasana-, implisiittisen, valtuutuskoodivirran, tokenien uusimisen ja turvallisuuskäytännöt vankkojen API:iden rakentamiseen.
FastAPI OAuth2-toteutus: Kattava todennusvirtaohje
Nykypäivän digitaalisessa maisemassa API:iden suojaaminen on ensiarvoisen tärkeää. OAuth2 (Open Authorization) on muodostunut alan standardiksi delegoituun valtuutukseen, jonka avulla käyttäjät voivat myöntää rajallisen pääsyn resursseihinsa jakamatta tunnistetietojaan. FastAPI, moderni, suorituskykyinen Python-web-kehys, tekee OAuth2-todennuksen toteuttamisesta helppoa. Tämä kattava opas käy läpi eri OAuth2-virrat ja näyttää, kuinka ne integroidaan FastAPI-sovellukseesi, varmistaen, että API:si pysyy turvallisena ja saavutettavana.
OAuth2-käsitteiden ymmärtäminen
Ennen koodiin syventymistä selvitetään ensin OAuth2:n ydinkäsitteet:
- Resurssin omistaja: Käyttäjä, joka omistaa tiedot ja myöntää pääsyn.
- Asiakas (Client): Sovellus, joka pyytää pääsyä resurssin omistajan tietoihin. Tämä voi olla verkkosovellus, mobiilisovellus tai mikä tahansa muu palvelu.
- Valtuutuspalvelin (Authorization Server): Todentaa resurssin omistajan ja myöntää valtuutuksen asiakkaalle.
- Resurssipalvelin (Resource Server): Isännöi suojattuja resursseja ja tarkistaa käyttöoikeusavaimen (access token) ennen pääsyn myöntämistä.
- Käyttöoikeusavain (Access Token): Tunniste, joka edustaa resurssin omistajan asiakkaalle myöntämää valtuutusta.
- Uusimisavain (Refresh Token): Pitkäikäinen tunniste, jota käytetään uusien käyttöoikeusavainten hankkimiseen ilman, että resurssin omistajan tarvitsee valtuuttaa uudelleen.
- Laajuudet (Scopes): Määrittelevät asiakkaan pyytämät tarkat käyttöoikeudet.
OAuth2-virrat: Oikean lähestymistavan valitseminen
OAuth2 määrittelee useita valtuutusvirtoja, joista jokainen sopii eri tilanteisiin. Tässä erittely yleisimmistä virtauksista ja niiden käyttöajankohdista:
1. Salasana (Resource Owner Password Credentials) -virta
Kuvaus: Asiakas hankkii käyttöoikeusavaimen suoraan valtuutuspalvelimelta antamalla resurssin omistajan käyttäjätunnuksen ja salasanan. Käyttötapaus: Erittäin luotetut sovellukset, kuten ensisijaiset mobiilisovellukset. Sitä tulisi käyttää vain, kun muut virrat eivät ole mahdollisia. Edut: Helppo toteuttaa. Haitat: Vaatii asiakkaan käsittelemään resurssin omistajan tunnistetietoja, mikä lisää paljastumisriskiä, jos asiakas vaarantuu. Vähemmän turvallinen kuin muut virrat. Esimerkki: Yrityksen oma mobiilisovellus, joka käyttää sen sisäistä API:a.
Toteutus FastAPI:ssa:
Asenna ensin tarvittavat paketit:
pip install fastapi uvicorn python-multipart passlib[bcrypt] python-jose[cryptography]
Luodaan nyt perusesimerkki:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
app = FastAPI()
# Korvaa vahvalla, satunnaisesti luodulla salaisella avaimella
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# Salasanan hajautuksen määritykset
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Väliaikainen käyttäjätietokanta (käytä oikeaa tietokantaa tuotannossa)
users = {
"johndoe": {
"username": "johndoe",
"hashed_password": pwd_context.hash("password123"),
"scopes": ["read", "write"]
}
}
# Funktio salasanan varmistamiseksi
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
# Funktio käyttöoikeusavaimen luomiseksi
def create_access_token(data: dict, expires_delta: timedelta):
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# OAuth2-päätepiste tokenin luomista varten
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = users.get(form_data.username)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Virheellinen käyttäjätunnus tai salasana",
headers={"WWW-Authenticate": "Bearer"},
)
if not verify_password(form_data.password, user["hashed_password"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Virheellinen käyttäjätunnus tai salasana",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"], "scopes": user["scopes"]},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}
# Riippuvuus pyyntöjen todennukseen
async def get_current_user(token: str):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Tunnisteita ei voitu varmistaa",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = users.get(username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user = Depends(get_current_user)):
return current_user
# Esimerkki suojatusta päätepisteestä
@app.get("/users/me")
async def read_users_me(current_user = Depends(get_current_active_user)):
return {"username": current_user["username"], "scopes": current_user["scopes"]}
Selitys:
- Riippuvuudet: Käytämme `fastapi.security.OAuth2PasswordRequestForm` käyttäjätunnuksen ja salasanan käsittelyyn.
- Salasanan hajautus: `passlib` -kirjastoa käytetään salasanojen turvalliseen hajauttamiseen ja varmistamiseen. Älä koskaan tallenna salasanoja selväkielisinä!
- JWT-luonti: `python-jose` -kirjastoa käytetään JSON Web Tokenien (JWT) luomiseen ja varmistamiseen.
- `/token`-päätepiste: Tämä päätepiste käsittelee kirjautumisprosessia. Se validoi käyttäjätunnuksen ja salasanan, ja jos ne ovat kelvollisia, luo käyttöoikeusavaimen.
- `get_current_user`-riippuvuus: Tämä funktio varmistaa käyttöoikeusavaimen ja hakee käyttäjän.
- `/users/me`-päätepiste: Tämä on suojattu päätepiste, joka vaatii kelvollisen käyttöoikeusavaimen pääsyyn.
2. Implisiittinen virta
Kuvaus: Asiakas saa käyttöoikeusavaimen suoraan valtuutuspalvelimelta resurssin omistajan todennuksen jälkeen. Käyttöoikeusavain palautetaan URL-fragmentissa. Käyttötapaus: Yhden sivun sovellukset (SPA) ja muut selainpohjaiset sovellukset, joissa asiakassalaisuuksien tallentaminen ei ole mahdollista. Edut: Helppo selainpohjaisille sovelluksille. Haitat: Vähemmän turvallinen kuin muut virrat, koska käyttöoikeusavain on näkyvillä URL:ssä. Uusimisavainta ei myönnetä. Esimerkki: JavaScript-sovellus, joka käyttää sosiaalisen median API:a.
Toteutusnäkökohdat FastAPI:ssa:
Vaikka FastAPI ei suoraan käsittele Implisiittisen virran käyttöliittymäpuolta (koska se on pääasiassa taustakehys), käyttäisit taustakehystä, kuten React, Vue tai Angular, todennusvirran hallintaan. FastAPI toimisi pääasiassa Resurssipalvelimena.
Yksinkertaistettu taustapalvelu (FastAPI - Resurssipalvelin) -esimerkki:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2AuthorizationCodeBearer
from jose import JWTError, jwt
app = FastAPI()
# Korvaa vahvalla, satunnaisesti luodulla salaisella avaimella
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
# Väliaikainen käyttäjätietokanta (käytä oikeaa tietokantaa tuotannossa)
users = {
"johndoe": {
"username": "johndoe",
"scopes": ["read", "write"]
}
}
# OAuth2-skeema - käyttää AuthorizationCodeBeareria tokenin varmistamiseen
oauth2_scheme = OAuth2AuthorizationCodeBearer(authorizationUrl="/auth", tokenUrl="/token") # Nämä URL:t käsitellään valtuutuspalvelimella (ei tämä FastAPI-sovellus).
# Riippuvuus pyyntöjen todennukseen
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Tunnisteita ei voitu varmistaa",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = users.get(username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user = Depends(get_current_user)):
return current_user
# Esimerkki suojatusta päätepisteestä
@app.get("/users/me")
async def read_users_me(current_user = Depends(get_current_active_user)):
return {"username": current_user["username"], "scopes": current_user["scopes"]}
Tärkeitä pisteitä Implisiittisen virran osalta FastAPI:n kanssa:
- Valtuutuspalvelimen rooli: Varsinainen valtuutus ja tokenin myöntäminen tapahtuu erillisellä valtuutuspalvelimella. FastAPI toimii resurssipalvelimena, joka varmistaa tokenin.
- Käyttöliittymän käsittely: Käyttöliittymäsovellus (esim. React, Vue) hoitaa ohjauksen valtuutuspalvelimelle, käyttäjän kirjautumisen ja käyttöoikeusavaimen hankkimisen URL-fragmentista.
- Turvallisuusnäkökohdat: Koska käyttöoikeusavain paljastuu URL:ssä, HTTPS:n käyttö ja lyhyen tokenin voimassaoloaika ovat kriittisiä. Implisiittistä virtaa tulisi välttää, jos mahdollista, ja suosia valtuutuskoodivirtaa PKCE:n kanssa.
3. Valtuutuskoodivirta
Kuvaus: Asiakas saa ensin valtuutuskoodin valtuutuspalvelimelta, jonka se sitten vaihtaa käyttöoikeusavaimeen. Tämä virta sisältää ohjauksen asiakkaalta valtuutuspalvelimelle ja takaisin. Käyttötapaus: Verkkosovellukset ja mobiilisovellukset, joissa asiakassalaisuus voidaan tallentaa turvallisesti. Edut: Turvallisempi kuin Implisiittinen virta, koska käyttöoikeusavainta ei paljasteta suoraan selaimessa. Haitat: Monimutkaisempi toteuttaa kuin Implisiittinen virta. Esimerkki: Kolmannen osapuolen sovellus, joka pyytää pääsyä käyttäjän Google Drive -tietoihin.
Valtuutuskoodivirta PKCE:n (Proof Key for Code Exchange) kanssa:
PKCE on laajennus Valtuutuskoodivirtaan, joka lieventää valtuutuskoodin sieppauksen riskiä. Sitä suositellaan vahvasti mobiilisovelluksille ja SPA:ille, koska se ei vaadi asiakasta tallentamaan salaisuutta.
Toteutusnäkökohdat FastAPI:ssa: Kuten Implisiittisen virran tapauksessa, FastAPI toimisi pääasiassa Resurssipalvelimena tässä virtauksessa. Erillinen valtuutuspalvelin vastaa todennuksesta ja valtuutuskoodin myöntämisestä.
Yksinkertaistettu taustapalvelu (FastAPI - Resurssipalvelin) -esimerkki (samanlainen kuin Implisiittinen virta):
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2AuthorizationCodeBearer
from jose import JWTError, jwt
app = FastAPI()
# Korvaa vahvalla, satunnaisesti luodulla salaisella avaimella
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
# Väliaikainen käyttäjätietokanta (käytä oikeaa tietokantaa tuotannossa)
users = {
"johndoe": {
"username": "johndoe",
"scopes": ["read", "write"]
}
}
# OAuth2-skeema - käyttää AuthorizationCodeBeareria tokenin varmistamiseen
oauth2_scheme = OAuth2AuthorizationCodeBearer(authorizationUrl="/auth", tokenUrl="/token") # Nämä URL:t käsitellään valtuutuspalvelimella.
# Riippuvuus pyyntöjen todennukseen
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Tunnisteita ei voitu varmistaa",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = users.get(username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user = Depends(get_current_user)):
return current_user
# Esimerkki suojatusta päätepisteestä
@app.get("/users/me")
async def read_users_me(current_user = Depends(get_current_active_user)):
return {"username": current_user["username"], "scopes": current_user["scopes"]}
Tärkeitä pisteitä Valtuutuskoodivirran PKCE:n kanssa FastAPI:n kanssa:
- Valtuutuspalvelimen rooli: Valtuutuspalvelin vastaa valtuutuskoodin luomisesta, PKCE-koodin vahvistajan varmistamisesta ja käyttöoikeusavaimen myöntämisestä.
- Käyttöliittymän käsittely: Käyttöliittymäsovellus luo koodin vahvistajan ja koodin haasteen, ohjaa käyttäjän valtuutuspalvelimelle, vastaanottaa valtuutuskoodin ja vaihtaa sen käyttöoikeusavaimeen.
- Lisääntynyt turvallisuus: PKCE estää valtuutuskoodin sieppausyritykset, mikä tekee siitä sopivan SPA:ille ja mobiilisovelluksille.
- Suositeltava lähestymistapa: Valtuutuskoodivirta PKCE:n kanssa on yleensä turvallisin ja suositeltavin virta moderneille verkkosovelluksille ja mobiilisovelluksille.
4. Asiakastunnistetietojen virta (Client Credentials Flow)
Kuvaus: Asiakas todennetaan suoraan valtuutuspalvelimeen käyttämällä omia tunnistetietojaan (asiakastunnus ja asiakassalaisuus) käyttöoikeusavaimen saamiseksi. Käyttötapaus: Koneiden välinen viestintä, kuten taustapalvelut, jotka käyttävät toisiaan. Edut: Helppo taustapalveluille. Haitat: Ei sovellu käyttäjän todennukseen. Esimerkki: Tietojen käsittelypalvelu, joka käyttää tietokantapalvelua.
Toteutus FastAPI:ssa:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from jose import JWTError, jwt
from datetime import datetime, timedelta
app = FastAPI()
# Korvaa vahvalla, satunnaisesti luodulla salaisella avaimella
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# Väliaikainen asiakastietokanta (käytä oikeaa tietokantaa tuotannossa)
clients = {
"client_id": {
"client_secret": "client_secret",
"scopes": ["read", "write"]
}
}
# Funktio käyttöoikeusavaimen luomiseksi
def create_access_token(data: dict, expires_delta: timedelta):
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# HTTP Basic -todennusskeema
security = HTTPBasic()
# Päätepiste tokenin luomista varten
@app.post("/token")
async def login(credentials: HTTPBasicCredentials = Depends(security)):
client = clients.get(credentials.username)
if not client:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Virheellinen asiakastunnus tai salaisuus",
headers={"WWW-Authenticate": "Basic"},
)
if credentials.password != client["client_secret"]:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Virheellinen asiakastunnus tai salaisuus",
headers={"WWW-Authenticate": "Basic"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": credentials.username, "scopes": client["scopes"]},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}
# Riippuvuus pyyntöjen todennukseen
async def get_current_client(token: str):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Tunnisteita ei voitu varmistaa",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
client_id: str = payload.get("sub")
if client_id is None:
raise credentials_exception
except JWTError:
raise credentials_exception
client = clients.get(client_id)
if client is None:
raise credentials_exception
return client
async def get_current_active_client(current_client = Depends(get_current_client)):
return current_client
# Esimerkki suojatusta päätepisteestä
@app.get("/data")
async def read_data(current_client = Depends(get_current_active_client)):
return {"message": "Data accessed by client: " + current_client["client_secret"]}
Selitys:
- HTTP Basic -todennus: Käytämme `fastapi.security.HTTPBasic` asiakkaan todennukseen.
- `/token`-päätepiste: Tämä päätepiste käsittelee asiakastodennusta. Se validoi asiakastunnuksen ja salaisuuden, ja jos ne ovat kelvollisia, luo käyttöoikeusavaimen.
- `get_current_client`-riippuvuus: Tämä funktio varmistaa käyttöoikeusavaimen ja hakee asiakkaan.
- `/data`-päätepiste: Tämä on suojattu päätepiste, joka vaatii kelvollisen käyttöoikeusavaimen pääsyyn.
Tokenien uusiminen
Käyttöoikeusavaimilla on tyypillisesti lyhyt voimassaoloaika, jotta väärennettyjen tokenien vaikutus minimoitaisiin. Uusimisavaimet ovat pitkäikäisiä tunnisteita, joita voidaan käyttää uusien käyttöoikeusavainten hankkimiseen ilman, että käyttäjän tarvitsee valtuuttaa uudelleen.
Toteutusnäkökohdat:
- Uusimisavainten tallentaminen: Uusimisavaimet tulisi tallentaa turvallisesti, mieluiten salattuna tietokantaan.
- Uusimisavainten päätepiste: Luo erillinen päätepiste (esim. `/refresh_token`) uusimisavainpyyntöjen käsittelyyn.
- Uusimisavainten mitätöinti: Toteuta mekanismi uusimisavainten mitätöimiseksi, jos ne vaarantuvat tai niitä ei enää tarvita.
Esimerkki (Salasanavirtaesimerkin laajennus):
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
import secrets # Turvallisten satunnaisten merkkijonojen luomiseen
app = FastAPI()
# Korvaa vahvalla, satunnaisesti luodulla salaisella avaimella
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
REFRESH_TOKEN_EXPIRE_DAYS = 30 # Pidempi voimassaoloaika uusimisavaimille
# Salasanan hajautuksen määritykset
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Väliaikainen käyttäjätietokanta (käytä oikeaa tietokantaa tuotannossa)
users = {
"johndoe": {
"username": "johndoe",
"hashed_password": pwd_context.hash("password123"),
"scopes": ["read", "write"],
"refresh_token": None # Tallennetaan uusimisavain tähän
}
}
# Funktio salasanan varmistamiseksi (sama kuin ennen)
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
# Funktio käyttöoikeusavaimen luomiseksi (sama kuin ennen)
def create_access_token(data: dict, expires_delta: timedelta):
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# Funktio uusimisavaimen luomiseksi
def create_refresh_token():
return secrets.token_urlsafe(32) # Luo turvallinen satunnainen merkkijono
# OAuth2-päätepiste tokenin luomista varten
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = users.get(form_data.username)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Virheellinen käyttäjätunnus tai salasana",
headers={"WWW-Authenticate": "Bearer"},
)
if not verify_password(form_data.password, user["hashed_password"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Virheellinen käyttäjätunnus tai salasana",
headers={"WWW-Authenticate": "Bearer"},
)
# Luo uusimisavain ja tallenna se (turvallisesti tietokantaan todellisessa ympäristössä)
refresh_token = create_refresh_token()
user["refresh_token"] = refresh_token # Tallenna käyttäjäobjektiin nyt (TURVATON TUOTANNOSSA)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"], "scopes": user["scopes"]},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer", "refresh_token": refresh_token}
# Päätepiste käyttöoikeusavaimen uusimiseen
@app.post("/refresh_token")
async def refresh_access_token(refresh_token: str):
# Etsi käyttäjä uusimisavaimella (turvallisesti kysy tietokannasta)
user = next((user for user in users.values() if user["refresh_token"] == refresh_token), None)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Virheellinen uusimisavain",
headers={"WWW-Authenticate": "Bearer"},
)
# Luo uusi käyttöoikeusavain
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"], "scopes": user["scopes"]},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}
# Riippuvuus pyyntöjen todennukseen (sama kuin ennen)
async def get_current_user(token: str):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Tunnisteita ei voitu varmistaa",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = next((user for user in users.values() if user["username"] == username), None)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user = Depends(get_current_user)):
return current_user
# Esimerkki suojatusta päätepisteestä (sama kuin ennen)
@app.get("/users/me")
async def read_users_me(current_user = Depends(get_current_active_user)):
return {"username": current_user["username"], "scopes": current_user["scopes"]}
Tärkeitä turvallisuusmuistiinpanoja:
- Uusimisavainten tallentaminen: Esimerkki tallentaa uusimisavaimen muistiin (turvattomasti). Tuotantoympäristössä tallenna uusimisavaimet turvallisesti tietokantaan, mieluiten salattuna.
- Uusimisavainten rotaatio: Harkitse uusimisavainten rotaation toteuttamista. Kun uusimisavainta on käytetty, luo uusi uusimisavain ja mitätöi vanha. Tämä rajoittaa vaarantuneiden uusimisavainten vaikutusta.
- Auditointi: Kirjaa uusimisavainten käyttö epäilyttävän toiminnan havaitsemiseksi.
Turvallisuuskäytännöt
OAuth2:n toteuttaminen on vain ensimmäinen askel. Turvallisuuskäytäntöjen noudattaminen on ratkaisevan tärkeää API:si ja käyttäjätietojesi suojaamiseksi.
- Käytä HTTPS:ää: Käytä aina HTTPS:ää tietoliikenteen salaamiseen asiakkaan, valtuutuspalvelimen ja resurssipalvelimen välillä.
- Varmista syötteet: Varmista kaikki syötetiedot perusteellisesti estääksesi syöhyökkäykset.
- Nopeusrajoitukset: Toteuta nopeusrajoitukset estääksesi raakavoimahyökkäykset.
- Päivitä riippuvuudet säännöllisesti: Pidä FastAPI-kehys ja kaikki riippuvuudet ajan tasalla turvallisuusaukkojen korjaamiseksi.
- Käytä vahvoja salaisuuksia: Luo vahvoja, satunnaisia salaisuuksia asiakassalaisuuksillesi ja JWT-allekirjoitusavaimillesi. Tallenna nämä salaisuudet turvallisesti (esim. käyttämällä ympäristömuuttujia tai salaisuuksien hallintajärjestelmää).
- Seuraa ja kirjaa: Seuraa API:si epäilyttävää toimintaa ja kirjaa kaikki todennus- ja valtuutustapahtumat.
- Ota käyttöön vähimpien oikeuksien periaate: Myönnä asiakkaille vain tarvittavat käyttöoikeudet (scopes).
- Asianmukainen virheenkäsittely: Vältä arkaluonteisten tietojen paljastamista virheilmoituksissa.
- Harkitse hyvin arvioidun OAuth2-kirjaston käyttöä: Sen sijaan, että toteuttaisit OAuth2:n alusta alkaen, harkitse hyvin arvioidun kirjaston, kuten Authlibin, käyttöä. Authlib tarjoaa vankemman ja turvallisemman OAuth2-toteutuksen.
Perusteiden lisäksi: Edistyneitä näkökohtia
Kun perus OAuth2-toteutus on valmis, harkitse näitä edistyneitä aiheita:
- Suostumuksen hallinta: Tarjoa käyttäjille selkeä ja granulaarinen kontrolli myönnetyistä käyttöoikeuksista asiakkaille.
- Delegoitu valtuutus: Toteuta tuki delegoidulle valtuutukselle, jonka avulla käyttäjät voivat valtuuttaa asiakkaita toimimaan heidän puolestaan.
- Monitekijätodennus (MFA): Integroi MFA turvallisuuden parantamiseksi.
- Yhdistetty identiteetti: Tue todennusta kolmannen osapuolen identiteettipalveluntarjoajien (esim. Google, Facebook, Twitter) kautta.
- Dynaaminen asiakkaan rekisteröinti: Salli asiakkaiden rekisteröityä dynaamisesti valtuutuspalvelimeesi.
Yhteenveto
OAuth2-todennuksen toteuttaminen FastAPI:n kanssa on tehokas tapa suojata API:si ja suojata käyttäjätietoja. Ymmärtämällä eri OAuth2-virrat, toteuttamalla turvallisuuskäytäntöjä ja harkitsemalla edistyneitä aiheita voit rakentaa vankkoja ja turvallisia API:ita, jotka vastaavat käyttäjiesi ja sovellustesi tarpeisiin. Muista valita oikea virta tiettyyn käyttötapaukseesi, priorisoida turvallisuus ja seurata sekä parantaa todennusjärjestelmääsi jatkuvasti. Vaikka annetut esimerkit esittelevät perusperiaatteita, mukauta ne aina omiin vaatimuksiisi ja konsultoi turvallisuusasiantuntijoita perusteellista tarkistusta varten.