+
    „WÝj@+  ã                  óö  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIHtH	t	H
t
 ^ RIHt ^ RIHt ^ RIHt RtRtR	tR
tRtR;tR<tR=t^<t]P4                  ! R4      tR R ltR R ltR R ltR R ltRR/R R llt RR/R R llt!RRRR/R R llt"R R  lt#RR/R! R" llt$R# R$ lt%RR/R% R& llt&RR/R' R( llt'R) R* lt(R+ R, lt)R- R. lt*R/ R0 lt+R1 R2 lt,RR/R3 R4 llt-R5 R6 lt.RR/R7 R8 llt/R9 R: lt0R# )>u$  Cookie helpers for dashboard auth.

All HttpOnly, ``SameSite=Lax`` unless noted, Path = proxy prefix or /: ``hermes_session_at``
(access token; Max-Age = token TTL), ``hermes_session_rt`` (rotating refresh token; written only
when the provider returned one, always cleared on logout/expiry), ``hermes_session_provider``
(non-secret routing hint so an RT is not handed to the wrong provider), ``hermes_session_pkce``
(PKCE state + CSRF nonce + provider hint, 10 min; ``SameSite=None; Secure`` over HTTPS because it
is set on the /auth/login 302 and must survive the cross-site redirect chain â€” Chromium drops Lax
cookies set on such a 302, crbug 40508226), ``hermes_sso_attempt`` (auto-SSO loop guard, 60 s).
``Secure`` only when ``request.url.scheme`` is https. Cookie-prefix hardening per
draft-west-cookie-prefixes: bare name over HTTP; ``__Host-`` on gated HTTPS with Path=/;
``__Secure-`` behind a proxy prefix (``__Host-`` forbids Path != /). Setters and readers BOTH
resolve the name via :func:`_resolved_name` â€” a mismatch silently breaks sessions.
)ÚannotationsN)ÚLiteralÚOptionalÚTuple)Úunquote)ÚRequest)ÚResponseÚhermes_session_atÚhermes_session_rtÚhermes_session_providerÚhermes_session_pkceÚhermes_sso_attemptÚ z^[A-Za-z0-9_-]+={0,2}$c               ó(   € V ^8„  d   QhRRRRRRRR/# )é   ÚbareÚstrÚ	use_httpsÚboolÚprefixÚreturn© )Úformats   "ÚUC:\Users\johnn\AppData\Local\hermes\hermes-agent\hermes_cli\dashboard_auth\cookies.pyÚ__annotate__r   /   s(   € ÷ >ñ >˜ð >¨Dð >¸#ð >À#ñ >ó    c               ó:   € V'       g   V # V'       d   RV  2# RV  2# )zCCookie-prefix variant for the request shape (see module docstring).ú	__Secure-ú__Host-r   )r   r   r   s   &$$r   Ú_resolved_namer   /   s%   € çØˆß!'ˆY�t�fÐÐ=¨w°t°fÐ-=Ð=r   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   r   )r   s   "r   r   r   6   s   € ÷ %ñ %˜ð % ñ %r   c                ó   € V '       d   V # R# )zL``Path=/hermes`` under a proxy prefix (no leak to sibling apps), else ``/``.Ú/r   )r   s   &r   Ú_cookie_pathr#   6   s   € çˆ6Ð$ Ð$r   c               ó$   € V ^8„  d   QhRRRRRR/# ©r   r   r   r   r   r   Údictr   )r   s   "r   r   r   ;   s!   € ÷ ñ  ð ¨cð °dñ r   c                óB   € R RRRR\        V4      /pV '       d   RVR&   V# )ÚhttponlyTÚsamesiteÚlaxÚpathÚsecure)r#   ©r   r   Úattrss   $$ r   Ú_common_attrsr/   ;   s*   € Ø˜t Z°¸ÄÈVÓ@TÐU€EßØˆˆh‰Ø€Lr   c               ó$   € V ^8„  d   QhRRRRRR/# r%   r   )r   s   "r   r   r   B   s!   € ÷ ñ ˜dð ¨Cð °Dñ r   c                ó8   € \        WR7      pV '       d   RVR&   V# )zwAttributes shared by the PKCE set AND clear paths (a shape mismatch
means the browser silently keeps the stale cookie).©r   r   Únoner)   )r/   r-   s   $$ r   Ú_pkce_attrsr4   B   s    € ô  IÔ=€EßØ"ˆˆjÑØ€Lr   r.   c               ó8   € V ^8„  d   QhRRRRRRRRRR	R
RRRRR/# )r   Úresponser   r   r   ÚvalueÚmax_ageÚintr   r   r   r.   zdict | Noner   ÚNoner   )r   s   "r   r   r   K   sR   € ÷ _ñ _�8ð _ 3ð _¨sð _Àð _Øð_Ø"%ð_Ø.9ð_ØEIñ_r   c          	     óh   € V P                   ! \        WVR 7      V3RV/Ve   TM\        WER 7      B  R# )r2   r8   N)Ú
set_cookier   r/   )r6   r   r7   r8   r   r   r.   s   &&&$$$$r   Ú_setr=   K   s;   € à×ÒÜ�t¸Ô@À%ñ_ØQXð_àÒ%‰5¬=À9Ô+\ô_r   r   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   r6   r   Úproviderr   r   r   r   r   r:   r   )r   s   "r   r   r   R   s4   € ÷ 1ñ 1Øð1Ø%(ð1Ø59ð1ØCFð1ØPTñ1r   c          	     óJ   € V'       d   \        V \        V\        W#R7       R# R# )z?Persist the non-secret provider routing hint for token refresh.©r8   r   r   N)r=   ÚSESSION_PROVIDER_COOKIEÚ_RT_MAX_AGE)r6   r?   r   r   s   &$$$r   Úset_session_provider_cookierD   R   s    € ÷ ÜˆXÔ.°Ä+Ø ÷	1ñ r   r?   c               ó8   € V ^8„  d   QhRRRRRRRRRR	R
RRRRR/# )r   r6   r   Úaccess_tokenr   Úrefresh_tokenÚaccess_token_expires_inr9   r   r   r   r?   r   r:   r   )r   s   "r   r   r   Z   sV   € ÷ añ aØðaØ),ðaØ=@ðaØ[^ðaàðaà ðaà14ðaà>Bñar   c          	     ó†   € \        V \        WWER7       V'       d   \        V \        V\        WER7       \	        WWER7       R# )uú  ``access_token_expires_in`` is seconds (the provider's reported TTL). An empty
``refresh_token`` means "don't persist the RT cookie" â€” a literal empty cookie would be dead
state at best, attack surface at worst.

Nous Portal issues a 24h rotating refresh token (hermes #37247); a provider that omits it returns
``Session.refresh_token == ""`` and we simply don't persist the RT cookie â€” the session then behaves as
access-token-only until the AT expires. No other branch changes between the two cases.
rA   )r?   r   r   N)r=   ÚSESSION_AT_COOKIEÚSESSION_RT_COOKIErC   rD   )r6   rF   rG   rH   r   r   r?   s   &$$$$$$r   Úset_session_cookiesrL   Z   s7   € ô 	ˆÔ$ lØõ-çÜˆXÔ(¨-ÄØ õ	1ä Ày×`r   c               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   r6   r   Ú	bare_namer   r   Úhttps_samesitez Literal['lax', 'strict', 'none']Ú
bare_attrsr&   r   r:   r   )r   s   "r   r   r   m   sD   € ÷ @ñ @Øð@Ø#&ð@Ø36ð@à4ð@àBFð@àKOñ@r   c               óš   € RR\        V4      33 F!  w  rVV P                  V V 2R^ VRVRR7       K#  	  V P                  ! VR3R^ /VB  R# )a[  Emit Max-Age=0 deletions for every plausible name variant (the setting request's shape is
unknown). Prefixed names are rejected by the browser unless they carry ``Secure`` (``__Host-``
additionally ``Path=/``), so those deletions always do; the bare deletion mirrors the setter's
shape (``bare_attrs``), which works on both HTTP and HTTPS origins.r   r   T)r8   r+   r(   r)   r,   r8   N)r   r"   )r#   r<   )r6   rN   r   rO   rP   Úvariantr+   s   &&$$$  r   Ú_clear_cookie_variantsrS   m   sh   € ð +¨[¼,ÀvÓ:NÐ,OÓP‰ˆØ×ÑØˆi˜	�{Ð# R°¸ÈØ#¨Dð 	ö 	2ñ Qð ×Ò˜	 2Ñ?¨qÐ?°JÔ?r   c               ó$   € V ^8„  d   QhRRRRRR/# ©r   r6   r   r   r   r   r:   r   )r   s   "r   r   r   {   s&   € ÷ Xñ X Hð X¸ð XÀdñ Xr   c          	     ón   € \        RVR7      p\        \        \        3 F  p\	        WVRVR7       K  	  R# )zIDelete the AT, RT and provider cookies (every name variant, active path).Fr2   r*   ©r   rO   rP   N)r/   rJ   rK   rB   rS   )r6   r   rP   Únames   &$  r   Úclear_session_cookiesrY   {   s4   € ä¨°vÔ>€JÜ"Ô$5Ô7NÓOˆÜØ 6¸%ÈJ÷	Xó Pr   c               ó    € V ^8„  d   QhRRRR/# )r   Úpartsúdict[str, str]r   r   r   )r   s   "r   r   r   ƒ   s   € ÷ Uñ U˜~ð U°#ñ Ur   c                óº   € \         P                  ! V RRR7      p\        P                  ! VP	                  R4      4      P                  R4      P                  R4      # )a2  Wire value ``base64url(JSON)``, no padding. The urlsafe alphabet is a strict subset of RFC
6265 cookie-octets, so http.cookies never quotes it (strict proxies such as Go net/http reject
the quoted form) and no value can collide with a delimiter; padding ``=`` would trigger
quoting, the parser restores it.T)Ú
separatorsÚ	sort_keyszutf-8ÚasciiÚ=)Ú,Ú:)ÚjsonÚdumpsÚbase64Úurlsafe_b64encodeÚencodeÚdecodeÚrstrip)r[   Úraws   & r   Úencode_pkce_payloadrl   ƒ   sG   € ô
 �*Š*�U z¸TÔ
B€CÜ×#Ò# C§J¡J¨wÓ$7Ó8×?Ñ?ÀÓH×OÑOÐPSÓTÐTr   c          
     ó,   € V ^8„  d   QhRRRRRRRRR	R
/# )r   r6   r   Úpayloadr\   r   r   r   r   r   r:   r   )r   s   "r   r   r   Œ   s;   € ÷ dñ dØðdØ$2ðdØ?CðdØMPðdØZ^ñdr   c               ó^   € \        V \        \        V4      \        W#\	        W#R7      R7       R# )zV``payload`` is the segment dict; see module docstring for the SameSite=None rationale.r2   )r8   r   r   r.   N)r=   ÚPKCE_COOKIErl   Ú_PKCE_MAX_AGEr4   )r6   rn   r   r   s   &$$$r   Úset_pkce_cookierr   Œ   s%   € ô 	ˆ”;Ô 3°GÓ <ÄmØ´;ÈÔ3b÷dr   c               ó(   € V ^8„  d   QhRRRRRRRR/# ©	r   r6   r   r   r   r   r   r   r:   r   )r   s   "r   r   r   “   s/   € ÷ Dñ D ð D¸ð DÀcð DÐSWñ Dr   c               óB   € \        V \        VR\        WR7      R7       R# )zQDelete every PKCE cookie variant (prefixed ones carry ``Secure; SameSite=None``).r3   r2   rW   N)rS   rp   r4   ©r6   r   r   s   &$$r   Úclear_pkce_cookierw   “   s   € äØ”+ f¸VÜ¨ÔB÷Dr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úrequestr   rN   r   r   úOptional[str]r   )r   s   "r   r   r   š   s!   € ÷ *ñ * ð *°Sð *¸]ñ *r   c                óJ   a a€ \        R VV 3R l\         4        4       R4      # )zYTry every prefix variant (the reading request may not match the setting request's shape).c              3  ó2   "  € T F  pVf   K	  Vx € K  	  R # 5i©Nr   )Ú.0Úvs   & r   Ú	<genexpr>Ú&_read_with_fallback.<locals>.<genexpr>œ   s   é € ð #Ñ\�qØ÷ ’Ó\ùs   ‚�
c              3  ób   <"  € T F$  pSP                   P                  V S 24      x € K&  	  R # 5ir}   )ÚcookiesÚget)r~   ÚprN   ry   s   & €€r   r€   r�   œ   s,   øé € Ð\É^È˜WŸ_™_×0Ñ0°A°3°y°kÐ1B×CÐCË^ùs   ƒ,/N)ÚnextÚ_NAME_VARIANTS)ry   rN   s   ffr   Ú_read_with_fallbackrˆ   š   s%   ù€ äñ #Õ\Í^Ô\ó #Ø$(ó*ð *r   c               ó    € V ^8„  d   QhRRRR/# )r   ry   r   r   z#tuple[Optional[str], Optional[str]]r   )r   s   "r   r   r       s   € ÷ 9ñ 9 'ð 9Ð.Qñ 9r   c                óB   € \        V \        4      \        V \        4      3# )z:Returns (access_token, refresh_token), either may be None.)rˆ   rJ   rK   ©ry   s   &r   Úread_session_cookiesrŒ       s$   € ô 	˜GÔ%6Ó7Ü˜GÔ%6Ó7ð9ð 9r   c               ó    € V ^8„  d   QhRRRR/# ©r   ry   r   r   rz   r   )r   s   "r   r   r   §   s   € ÷ Añ A 7ð A¨}ñ Ar   c                ó"   € \        V \        4      # )zEReturn the provider routing hint associated with the session cookies.)rˆ   rB   r‹   s   &r   Úread_session_providerr�   §   s   € ä˜wÔ(?Ó@Ð@r   c               ó    € V ^8„  d   QhRRRR/# rŽ   r   )r   s   "r   r   r   ¬   s   € ÷ 5ñ 5˜gð 5¨-ñ 5r   c                ó"   € \        V \        4      # r}   )rˆ   rp   r‹   s   &r   Úread_pkce_cookier“   ¬   s   € Ü˜w¬Ó4Ð4r   c               ó    € V ^8„  d   QhRRRR/# )r   rk   r   r   r\   r   )r   s   "r   r   r   °   s   € ÷ Lñ L˜Cð L Nñ Lr   c                óN  € \         P                  V 4      '       d©    V R\        V 4      ) ^,          ,          ,           p\        P                  ! \
        P                  ! VP                  R4      4      4      p\        V\        4      '       d8   VP                  4        UUu/ uF  w  r4\        V4      \        V4      bK  	  upp# RV 9   d   T M
\!        V 4      p\        R VP#                  R4       4       4      #   \        P                  \        \        3 d    Rp L¦i ; iu uppi )u  Inverse of :func:`encode_pkce_payload`. EVERY reader must go through this â€” reading the raw
wire value parses zero segments and silently disables the check it feeds (provider dispatch,
CSRF state, broker binding). Compatibility ladder for cookies minted by an older server
mid-upgrade: 1. base64url(JSON); 2. flat form with raw ``;`` delimiters, split WITHOUT
unquoting (the ``next`` segment carries its own URL-encoding); 3. URL-encoded flat form,
unquote once then split. A NEW cookie hitting an OLD server fails the state check.

1. **base64url(JSON)** (current): the wire value is pure urlsafe base64 that decodes to a JSON object.
Legacy forms can never match â€” they always contain ``%`` (URL-encoded, #99176) or a raw ``;`` (oldest
flat form), both outside the base64url alphabet. Split as-is WITHOUT unquoting the payload â€” the
``next`` segment carries its own single URL-encoding, and unquoting here would turn a ``%3B`` inside it
into a bogus delimiter and truncate the post-login target. Neither newer format can contain a raw ``;``.
ra   r`   NÚ;c              3  óX   "  € T F   pR V9   g   K  VP                  R ^4      x € K"  	  R# 5i)ra   N)Úsplit)r~   Úsegs   & r   r€   Ú%parse_pkce_payload.<locals>.<genexpr>Ç   s'   é € ÐK©_ cÀÀsÁ
Ô!�—	‘	˜#˜q×!Ð!«_ùs   ‚	*�*)Ú
_B64URL_REÚmatchÚlenrd   Úloadsrf   Úurlsafe_b64decoderh   ÚbinasciiÚErrorÚ
ValueErrorÚUnicodeDecodeErrorÚ
isinstancer&   Úitemsr   r   r˜   )rk   ÚpaddedÚdecodedÚkr   Úflats   &     r   Úparse_pkce_payloadrª   °   sÞ   € ô ×Ñ˜×Òð	Ø˜3¤3 s£8 )¨a¥-Õ0Õ0ˆFÜ—j’j¤×!9Ò!9¸&¿-¹-ÈÓ:PÓ!QÓRˆGô �gœt×$Ò$Ø/6¯}©}¬Ô?©¡t q”C˜“FœC ›F’N©Ò?Ð?Ø˜”*‰3¤'¨#£,€DÜÑK¨T¯Z©Z¸¬_ÓKÓKÐKøô —‘¤
Ô,>Ð?ô 	ØŠGð	üó @s   �AC9 Â  D!Ã9"DÄDc               ó(   € V ^8„  d   QhRRRRRRRR/# rt   r   )r   s   "r   r   r   Ê   s)   € ÷ -ñ - Xð -¸Tð -È3ð -ÐX\ñ -r   c          	     ó6   € \        V \        R\        WR7       R# )z>Set the auto-SSO loop-guard marker; only its presence matters.Ú1rA   N)r=   ÚSSO_ATTEMPT_COOKIEÚ_SSO_ATTEMPT_MAX_AGErv   s   &$$r   Úset_sso_attempt_cookier°   Ê   s   € äˆÔ% sÔ4HØ÷-r   c               ó    € V ^8„  d   QhRRRR/# rŽ   r   )r   s   "r   r   r   Ð   s   € ÷ <ñ < Wð <°ñ <r   c                ó"   € \        V \        4      # )zEReturn the auto-SSO marker value if present (any variant), else None.)rˆ   r®   r‹   s   &r   Úread_sso_attempt_cookier³   Ð   s   € ä˜wÔ(:Ó;Ð;r   c               ó$   € V ^8„  d   QhRRRRRR/# rU   r   )r   s   "r   r   r   Õ   s&   € ÷ Bñ B xð B¸Cð BÈñ Br   c               óD   € \        V \        VR\        RVR7      R7       R# )zYDelete the auto-SSO marker (every variant) so it never suppresses a later silent attempt.r*   Fr2   rW   N)rS   r®   r/   )r6   r   s   &$r   Úclear_sso_attempt_cookier¶   Õ   s   € äØÔ$¨VÀEÜ ¨5¸Ô@÷Br   c               ó    € V ^8„  d   QhRRRR/# )r   ry   r   r   r   r   )r   s   "r   r   r   Ü   s   € ÷ )ñ )˜'ð ) dñ )r   c                ó4   € V P                   P                  R8H  # )zY``Secure`` flag decision (honours ``X-Forwarded-Proto`` under uvicorn ``proxy_headers``).Úhttps)ÚurlÚschemer‹   s   &r   Údetect_httpsr¼   Ü   s   € à�;‰;×Ñ Ñ(Ð(r   )r   r   r   i �' iX  )1Ú__doc__Ú
__future__r   rf   r    rd   ÚreÚtypingr   r   r   Úurllib.parser   Úfastapir   Úfastapi.responsesr   rJ   rK   rB   rp   r®   r‡   rC   rq   r¯   Úcompiler›   r   r#   r/   r4   r=   rD   rL   rS   rY   rl   rr   rw   rˆ   rŒ   r�   r“   rª   r°   r³   r¶   r¼   r   r   r   Ú<module>rÅ      s0  ðñõ #ã Û Û Û 	ß +Ñ +Ý  å Ý &à'Ð Ø'Ð Ø3Ð Ø#€Ø)Ð ð .€ð  €Ø€ð Ð à�ZŠZÐ1Ó2€
õ>õ%õ
õð_Ø<@÷_ð1ØIK÷1ðaà#%ðaà79÷aõ&@ðX¸r÷ XõUðdØSU÷dðDÈB÷ Dõ*õ9õAõ
5õLð4-ÐQS÷ -õ<ð
BÀ"÷ B÷)r   