ЁЯкк рдзрдбрд╛ 06 тАФ Auth: рдХрд╛рдЙрдВрдЯрд░рд╡рд░рдЪреЗ рд╣реЙрд▓ рдкрд╛рд╕
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 06 ┬╖ рдорд╛рдЧреЗ: lesson-05-build-an-api ┬╖ рдкреБрдвреЗ: lesson-07-errors-idempotency
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ05, рдЖрдгрд┐ рддреНрдпрд╛рд╕реЛрдмрдд рдХреЛрдгрддреНрдпрд╛ slips рдХреЛрдг рджреЗрдК рд╢рдХрддреЛ: API keys,
bearer tokens / JWTs, OAuth, рдЖрдгрд┐ рдПрдХрд╕рд╛рд░рдЦреЗ рдирд╕рд▓реЗрд▓реЗ рджреЛрди
рдирдХрд╛рд░ (401 рд╡рд┐рд░реБрджреНрдз 403).
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
рд╕реВрдЪрдирд╛ рдлрд▓рдХ рд╡рд╛рдЪрд╛рдпрд▓рд╛ pass рд▓рд╛рдЧрдд рдирд╛рд╣реА тАФ рдХреЛрдгреАрд╣реА GET рдХрд░реВ рд╢рдХрддреЛ. рдкрдг рдиреЛрдВрджрд╡рд╣реАрдд
рд▓рд┐рд╣рд╛рдпрдЪреЗ (POST, PUT, DELETE) рддрд░ counter рд╡рд░ рд╣реЙрд▓ рдкрд╛рд╕
ЁЯкк рджрд╛рдЦрд╡рд╛рд╡рд╛ рд▓рд╛рдЧрддреЛ. рдЖрдордЪрд╛ counter рдЖрдЬ рдПрдХрд╛рдЪ рдкреНрд░рдХрд╛рд░рдЪрд╛ pass рд╕реНрд╡реАрдХрд╛рд░рддреЛ: рдПрдХ header,
X-API-Key: hall-pass-123.
- Pass рдЕрдЬрд┐рдмрд╛рдд рдирд╛рд╣реА тЖТ 401 unauthenticated: "рддреБрдореНрд╣реА рдХреЛрдг?"
- Pass рдЖрд╣реЗ, рдкрдг рд▓рд┐рд╣рд┐рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рдЕрд╕рд▓реЗрд▓рд╛ рдирд╛рд╣реА тЖТ 403 forbidden: "рддреБрдореНрд╣реА рдХреЛрдг рддреЗ рдорд▓рд╛ рдорд╛рд╣реАрдд рдЖрд╣реЗ; рддреБрдореНрд╣рд╛рд▓рд╛ рдкрд░рд╡рд╛рдирдЧреА рдирд╛рд╣реА."
рдЦрд▒реНрдпрд╛ рд╢рд╛рд│рд╛ рддреАрди рдкреНрд░рдХрд╛рд░рдЪреЗ pass рджреЗрддрд╛рдд, рдЖрдгрд┐ рддреБрдореНрд╣рд╛рд▓рд╛ рддрд┐рдиреНрд╣реА рднреЗрдЯрддреАрд▓:
- API key ЁЯФС тАФ рдПрдХрд╛ program рд▓рд╛ (canteen app) рджрд┐рд▓реЗрд▓реА рд▓рд╛рдВрдм random string. рд╕реЛрдкреА, рджреАрд░реНрдШрдХрд╛рд│ рдЯрд┐рдХрдгрд╛рд░реА, рд░рджреНрдж рдХрд░рддрд╛ рдпреЗрдгрд╛рд░реА. рддреА рдХрдзреАрдЪ URL, log, рдХрд┐рдВрд╡рд╛ browser рдордзреНрдпреЗ рдареЗрд╡реВ рдирдХрд╛ тАФ JavaScript рдордзреНрдпреЗ ship рд╣реЛрддрд╛рдХреНрд╖рдгреА рддреА рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рд╣реЛрддреЗ.
- Bearer token / JWT ЁЯОл тАФ рд╡реНрдпрдХреНрддреАрдиреЗ login рдХреЗрд▓реНрдпрд╛рдирдВрддрд░ рддрд┐рд▓рд╛ рджрд┐рд▓реЗрд▓рд╛ рд╕рд╣реА рдХреЗрд▓реЗрд▓рд╛,
рдореБрджрдд рд╕рдВрдкрдгрд╛рд░рд╛ pass:
Authorization: Bearer eyJтАж. Server рддреЛ рд╢реЛрдзрдгреНрдпрд╛рдРрд╡рдЬреА рд╕рд╣реА рддрдкрд╛рд╕рддреЛ; рдореБрджрдд рд╕рдВрдкрд▓реА рдХреА рддреЛ рдЖрдкреЛрдЖрдк рдЪрд╛рд▓рд╛рдпрдЪрд╛ рдмрдВрдж рд╣реЛрддреЛ. - OAuth 2 / OIDC ЁЯдЭ тАФ рдЕрдзрд┐рдХрд╛рд░ рд╕реЛрдкрд╡рдгреНрдпрд╛рдЪрд╛ рдЦреЗрд│: "рд╣реЗ calendar app рдорд╛рдЭреЗ events рд╡рд╛рдЪреВ рд╢рдХрддреЗ, рдкрдг delete рдХрд░реВ рд╢рдХрдд рдирд╛рд╣реА", user рдиреЗ provider рдЪреНрдпрд╛ рдкрд╛рдирд╛рд╡рд░ рджрд┐рд▓реЗрд▓реА, рдорд░реНрдпрд╛рджрд┐рдд (scoped), рд░рджреНрдж рдХрд░рддрд╛ рдпреЗрдгрд╛рд░реА рдкрд░рд╡рд╛рдирдЧреА. CI/CD рдЖрдгрд┐ AWS schools рдордзрд▓рд╛ OIDC badge рд╣рд╛рдЪ protocol machines рдордзреНрдпреЗ рд╡рд╛рдкрд░рд▓реЗрд▓рд╛ рдЖрд╣реЗ.
Pass рдХреЛрдгрддрд╛рд╣реА рдЕрд╕реЛ, рдирд┐рдпрдо рдЖрд╣реЗ least privilege: рдкреНрд░рддреНрдпреЗрдХ app рд▓рд╛ рдПрдХ key, рддреНрдпрд╛рд▓рд╛ рдЬреЗрд╡рдвреЗ рдХрд░рд╛рдпрдЪреЗ рддреЗрд╡рдвреНрдпрд╛рдкреБрд░рддреА рдорд░реНрдпрд╛рджрд┐рдд, рдард░рд▓реЗрд▓реНрдпрд╛ рд╡реЗрд│рд╛рдкрддреНрд░рдХрд╛рдиреЗ рдмрджрд▓рд▓реЗрд▓реА (rotate), рдЖрдгрд┐ рдЙрдШрдб рдЭрд╛рд▓реА рддреНрдпрд╛рдЪ рджрд┐рд╡рд╢реА рд░рджреНрдж рдХреЗрд▓реЗрд▓реА.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
r["ЁЯФУ GET (read)"] -->|"1 no pass needed"| ok1["200"]
w["тЬНя╕П POST / PUT / DELETE"] -->|"2 X-API-Key?"| chk{"pass?"}
chk -->|"none"| u["401 unauthenticated"]
chk -->|"wrong"| f["403 forbidden"]
chk -->|"right"| ok2["201 / 200 / 204"]
subgraph kinds["ЁЯкк three kinds of pass"]
k["ЁЯФС API key тАФ a program's"]
t["ЁЯОл bearer token / JWT тАФ a person's, expires"]
o["ЁЯдЭ OAuth / OIDC тАФ delegated, scoped"]
end
тЭУ рдХрд╛рдп
- Authentication = рдХреЛрдг рд╡рд┐рдЪрд╛рд░рдд рдЖрд╣реЗ (pass). Authorization = рддреНрдпрд╛рдВрдирд╛ рдХрд╛рдп
рдХрд░рдгреНрдпрд╛рдЪреА рдкрд░рд╡рд╛рдирдЧреА рдЖрд╣реЗ (scope).
401рдореНрд╣рдгрдЬреЗ authentication рдордзрд▓реЗ рдЕрдкрдпрд╢,403authorization рдордзрд▓реЗ тАФ рддреНрдпрд╛рдВрдЪреА рдЕрджрд▓рд╛рдмрджрд▓ рдХрдзреАрдЪ рдХрд░реВ рдирдХрд╛. - API keys header рдордзреВрди рдЬрд╛рддрд╛рдд (
X-API-KeyрдХрд┐рдВрд╡рд╛Authorization: ApiKey), query string рдордзреВрди рдХрдзреАрдЪ рдирд╛рд╣реА (URLs logs рдЖрдгрд┐ browser history рдордзреНрдпреЗ рдкреЛрд╣реЛрдЪрддрд╛рдд). Server рд╡рд░ рдлрдХреНрдд hash рдареЗрд╡рд╛; key рддрдпрд╛рд░ рдХрд░рддрд╛рдирд╛ рдПрдХрджрд╛рдЪ рджрд╛рдЦрд╡рд╛. - JWT:
header.payload.signature, base64. Payload рд╡рд╛рдЪрддрд╛ рдпреЗрддреЛ (рдЧреБрдкреНрдд рдирд╛рд╣реА!) тАФsub,exp,scope. рд╕рд╣реА рд╕рд╛рдВрдЧрддреЗ рдХреА рддреЛ рдХреЛрдгреА рджрд┐рд▓рд╛. рдХрдореА рдореБрджрдд + refresh tokens рд╣реА рдкреНрд░рдорд╛рдгрд┐рдд рдкрджреНрдзрдд рдЖрд╣реЗ. - OAuth 2: user рдЪреНрдпрд╛ рд╡рддреАрдиреЗ рдХрд╛рдо рдХрд░рдгрд╛рд▒реНрдпрд╛ apps рд╕рд╛рдареА authorization code flow; machine-to-machine рд╕рд╛рдареА client credentials; scopes рдкрд░рд╡рд╛рдирдЧреНрдпрд╛рдВрдЪреА рдирд╛рд╡реЗ рд╕рд╛рдВрдЧрддрд╛рдд. OIDC рддреНрдпрд╛рд╡рд░ "рд╣рд╛ user рдХреЛрдг" рдпрд╛рдЪреА рднрд░ рдШрд╛рд▓рддреЗ.
- Transport: TLS (
https://) рд╢рд┐рд╡рд╛рдп рд╣реЗ рд╕рдЧрд│реЗ рдирд┐рд░реБрдкрдпреЛрдЧреА рдЖрд╣реЗ тАФ рдореИрджрд╛рдирд╛рд╡рд░ рдУрд░рдбреВрди рд╕рд╛рдВрдЧрд┐рддрд▓реЗрд▓рд╛ pass рдореНрд╣рдгрдЬреЗ pass рдирд╛рд╣реА.
ЁЯкк рдкреНрд░рддреНрдпреЗрдХ рдкреНрд░рдХрд╛рд░рдЪрд╛ рд╣реЙрд▓ рдкрд╛рд╕: рд╣рд╛ рдзрдбрд╛ API key рд╡рд╛рдкрд░рддреЛ. Basic, Bearer tokens, JWT, session cookies, PKCE рд╕рд╣ OAuth 2.0, OpenID Connect, HMAC request signing рдЖрдгрд┐ mutual TLS рдкреНрд░рддреНрдпреЗрдХрд╛рдЪреЗ рдЪрд┐рддреНрд░ рдХрд╛рдврд▓реЗрд▓реЗ рдЖрд╣реЗ тАФ рдкреНрд░рддреНрдпреЗрдХ OAuth grant type, 401 рд╡рд┐рд░реБрджреНрдз 403, рдирд┐рд░реНрдгрдп-рдорд╛рд░реНрдЧрджрд░реНрд╢рди рдЖрдгрд┐ рдЪреБрдХрд╛ рдпрд╛рдВрд╕рд╣ тАФ рдзрдбрд╛ 14 тАФ рдкреНрд░рддреНрдпреЗрдХ authentication рдкрджреНрдзрдд рдордзреНрдпреЗ. рддреНрдпрд╛рддрд▓реНрдпрд╛ рд╕рд╛рдд рдЗрдереЗ рдЪрд╛рд▓рддрд╛рдд (
grant_type=passwordрдирд╛рдХрд╛рд░рдгрд╛рд▒реНрдпрд╛ OAuth token endpoint рд╕рд╣):python3 api/auth_demo.pyрдордЧpython3 api/auth_client.pyрддрд╛рд░реЗрд╡рд░рдЪрд╛ рдкреНрд░рддреНрдпреЗрдХ header, рдореБрджрдд рд╕рдВрдкрд▓реЗрд▓рд╛ token, рдмрдирд╛рд╡рдЯ JWT, рдЫреЗрдбрдЫрд╛рдб рдХреЗрд▓реЗрд▓реА рд╕рд╣реА рдЖрдгрд┐ рдкреБрдиреНрд╣рд╛ рдкрд╛рдард╡рд▓реЗрд▓реА (replayed) request рджрд╛рдЦрд╡рддреЗ.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг counter рд╣реАрдЪ рдПрдХрдореЗрд╡ рдЬрд╛рдЧрд╛ рдЖрд╣реЗ рдЬрд┐рдереЗ slip рдЖрдгрд┐ pass рджреЛрдиреНрд╣реА рджрд┐рд╕рддрд╛рдд.
рддрдкрд╛рд╕рдгреА рдЗрддрд░ рдХреБрдареЗрд╣реА рдареЗрд╡рд╛ тАФ UI рдордзреНрдпреЗ, database рдордзреНрдпреЗ тАФ рдЖрдгрд┐
рдХреЛрдгреАрддрд░реА рддреА рдЯрд╛рд│рдгрд╛рд░реЗ рджрд╛рд░ рд╢реЛрдзреЗрд▓рдЪ. рдЖрдгрд┐ 401/403 рдордзрд▓рд╛
рдЧреЛрдВрдзрд│ рдореНрд╣рдгрдЬреЗ рдлрдХреНрдд рдкрд╛рдВрдбрд┐рддреНрдп рдирд╛рд╣реА: 401 рд╡рд░ clients рдкреБрдиреНрд╣рд╛ login рдХрд░рддрд╛рдд рдЖрдгрд┐ 403 рд╡рд░ рдерд╛рдВрдмрддрд╛рдд;
рддреНрдпрд╛рдВрдЪреА рдЕрджрд▓рд╛рдмрджрд▓ рдХреЗрд▓реА рддрд░ users рдХрд╛рдпрдо рдлреЗрд▒реНрдпрд╛рдд рдЕрдбрдХрддрд╛рдд рдХрд┐рдВрд╡рд╛ рдЪреБрдХреАрдиреЗ рд╣рд╛рд░ рдорд╛рдирддрд╛рдд.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
school_api.py рдордзрд▓реЗ authorized(): header рдирд╛рд╣реА тЖТ 401, рдЪреБрдХреАрдЪреА рдХрд┐рдВрдордд тЖТ
403. Key environment рдордзрд▓реНрдпрд╛ SCHOOL_API_KEY рдордзреВрди рдпреЗрддреЗ. рдкреНрд░рддреНрдпреЗрдХ
write handler рддреЗ route рддрдкрд╛рд╕рдгреАрдЪреНрдпрд╛ рдирдВрддрд░ рдЖрдгрд┐ body рд╡рд╛рдЪрдгреНрдпрд╛рдЪреНрдпрд╛ рдЖрдзреА
call рдХрд░рддреЛ тАФ рдзрдбрд╛ 05 рдордзрд▓рд╛ рдХреНрд░рдо.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
B=http://127.0.0.1:8080/v1; J='Content-Type: application/json'
curl -s -X POST $B/students -H "$J" -d '{"name":"Zoya","class":"3B"}' # 401
curl -s -X POST $B/students -H 'X-API-Key: wrong' -H "$J" -d '{"name":"Zoya","class":"3B"}' # 403
curl -s -X POST $B/students -H 'X-API-Key: hall-pass-123' -H "$J" -d '{"name":"Zoya","class":"3B"}' # 201
# rotate the pass without touching code:
SCHOOL_API_KEY=new-pass-456 python3 api/school_api.py 8081 &
curl -s -X DELETE http://127.0.0.1:8081/v1/students/1 -H 'X-API-Key: hall-pass-123' -o /dev/null -w '%{http_code}\n' # 403 тАФ old pass
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
hint: the hall pass is missing рд╕рд╣ 401, рдордЧ a pass, but not the right one рд╕рд╣ 403, рдордЧ 201. Port 8081 рд╡рд░ рдЬреБрдиреНрдпрд╛ key рд▓рд╛ 403 рдорд┐рд│рддреЛ тАФ рддреБрдореНрд╣реА environment variable рдиреЗ рдЖрдгрд┐ code рдордзреНрдпреЗ рд╢реВрдиреНрдп рдмрджрд▓ рдХрд░реВрди credential rotate рдХреЗрд▓реЗ.
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
рд╡рд╛рдЪрдгреНрдпрд╛рдЪреА рдЖрдгрд┐ рд▓рд┐рд╣рд┐рдгреНрдпрд╛рдЪреА рджрд╛рд░реЗ рд╡реЗрдЧрд│реА рдЖрд╣реЗрдд, рджреЛрди рдирдХрд╛рд░рд╛рдВрдЪреЗ рдЕрд░реНрде рд╡реЗрдЧрд│реЗ рдЖрд╣реЗрдд, рдЖрдгрд┐ pass code рдЪреНрдпрд╛ рдмрд╛рд╣реЗрд░ рд░рд╛рд╣рддреЛ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- pass рдирд╕рддрд╛рдирд╛
403(рдХрд┐рдВрд╡рд╛ рдЪреБрдХреАрдЪреНрдпрд╛ pass рд╕рд╛рдареА401) тАФ рджреЛрдиреНрд╣реА рдмрд╛рдмрддреАрдд clients рдЪреБрдХреАрдЪреЗ рд╡рд╛рдЧрддрд╛рдд - query strings рдордзрд▓реНрдпрд╛ рдХрд┐рдВрд╡рд╛ git рдордзреНрдпреЗ commit рдХреЗрд▓реЗрд▓реНрдпрд╛ keys тАФ рдЕрд╕реЗ рдШрдбрд▓реЗ (рдЖрдгрд┐ рддреЗ рдШрдбреЗрд▓рдЪ) рдХреА рд▓рдЧреЗрдЪ rotate рдХрд░рд╛
- browser app рдордзреНрдпреЗ API key тАФ user token рд╡рд╛рдкрд░рд╛; key ship рд╣реЛрддрд╛рдХреНрд╖рдгреА рд╕рд╛рд░реНрд╡рдЬрдирд┐рдХ рд╣реЛрддреЗ
- рдкреНрд░рддреНрдпреЗрдХ app рд╕рд╛рдареА рдПрдХрдЪ рд╕рд░реНрд╡рд╢рдХреНрддрд┐рдорд╛рди key тАФ рдкреНрд░рддреНрдпреЗрдХ app рд▓рд╛ рдПрдХ key, рдорд░реНрдпрд╛рджрд┐рдд, рдореНрд╣рдгрдЬреЗ рддреБрдореНрд╣реА рдПрдХрдЪ рд╡рд╛рдкрд░рдХрд░реНрддрд╛ рд░рджреНрдж рдХрд░реВ рд╢рдХрддрд╛
- рдЬрд┐рдереЗ pass рдкреНрд░рд╡рд╛рд╕ рдХрд░рддреЛ рддрд┐рдереЗ TLS рд╢рд┐рд╡рд╛рдп HTTP
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдмрд╣реБрддреЗрдХ рдЦрд░реЗ API breaches рд╣реБрд╢рд╛рд░реАрдЪреЗ рдирд╕рддрд╛рдд тАФ public repo рдордзрд▓реА key, рдХрдзреАрдЪ рдореБрджрдд рди рд╕рдВрдкрдгрд╛рд░рд╛ token, рдХрд┐рдВрд╡рд╛
authorized()call рдХрд░рд╛рдпрд▓рд╛ рд╡рд┐рд╕рд░рд▓реЗрд▓рд╛ endpoint. рд╡рд░рдЪреЗ рдХрдВрдЯрд╛рд│рд╡рд╛рдгреЗ рдирд┐рдпрдо рд╣реЗрдЪ рд╕рдВрдкреВрд░реНрдг рд╕рдВрд░рдХреНрд╖рдг рдЖрд╣реЗ.
тПня╕П рдкреБрдвреЗ
рднрд╛рдЧ 2 рд╕реБрд░реВ рд╣реЛрддреЛ: counter рдЦрд▒реНрдпрд╛ рдЬрдЧрд╛рдд рдЪрд╛рд▓рд╡рдгреЗ. рдЖрдзреА, рдирдореНрд░ рдирдХрд╛рд░ рдЖрдгрд┐ рдкрд╛рд╡рддреА рдХреНрд░рдорд╛рдВрдХ тАФ errors, retries рдЖрдгрд┐ idempotency keys.
git checkout lesson-07-errors-idempotency