+
    jHàjD   ã                  ó  a € 0 t $ R t^ RIHt ^ RIt^ RIHtHtHt ^ RI	H
t
HtHt ^ RIHtHtHtHtHtHtHtHtHtHtHtHtHt ]P6                  ! ]4      tRtRtRt ^t!R	s"R
]#R&    ! R R]4      t$R R lt%R R lt&R R lt'R# )ut  NousDashboardAuthProvider â€” Nous Portal OAuth (authorization-code + PKCE).

Implements ``nous-account-service/docs/agent-dashboard-oauth-contract.md``; registers only
when a client_id (``dashboard.oauth.client_id`` / ``HERMES_DASHBOARD_OAUTH_CLIENT_ID``, shape
``agent:{instance_id}``) is configured. Access tokens are RS256 JWTs verified against the
Portal JWKS with ``aud`` = bare client_id. Portal issues a 24h *rotating* refresh token with
reuse detection: the middleware MUST persist ``Session.refresh_token`` back to the cookie on
every refresh or the next refresh replays a rotated token and revokes the whole session.
)ÚannotationsN)ÚAnyÚDictÚOptional)Ú
LoginStartÚProviderErrorÚSession)ÚDEFAULT_TOKEN_LEEWAY_SECONDSÚJwtOAuthProviderÚSkipRegistrationÚexchange_tokenÚload_config_sectionÚparse_leewayÚpkce_login_startÚrefresh_token_fromÚregister_providerÚresolve_env_or_cfgÚsession_from_claimsÚvalidate_redirect_uriÚ
verify_jwtzdashboard-auth-nouszhttps://portal.nousresearch.comzagent_dashboard:accessÚ ÚstrÚLAST_SKIP_REASONc                  ó–   € ] tR t^*tRtRtRtR]/R R lltR R lt	R	 R
 lt
R R ltR R ltRRRR/R R lltR R ltR R ltRtR# )ÚNousDashboardAuthProviderz7Nous Portal OAuth via authorization-code + PKCE (S256).ÚnouszNous ResearchÚtoken_leewayc               ó(   € V ^8„  d   QhRRRRRRRR/# )é   Ú	client_idr   Ú
portal_urlr   ÚfloatÚreturnÚNone© )Úformats   "ÚI/home/ubuntu/.hermes/hermes-agent/plugins/dashboard_auth/nous/__init__.pyÚ__annotate__Ú&NousDashboardAuthProvider.__annotate__0   s)   € ÷ &ñ & Sð &°cð &Èð &Ðrvñ &ó    c               	óV  € VP                  R 4      '       g   \        RV: 24      hWn        V\        R 4      R V n        VP                  R4      V n        \        V4      V n        V P                   R2V n	        V P                   R2V n
        V P                   R2V n        RV n        R# )úagent:z?client_id must match contract shape 'agent:{instance_id}', got NÚ/z/.well-known/jwks.jsonz/oauth/authorizez/api/oauth/token)Ú
startswithÚ
ValueErrorÚ
_client_idÚlenÚ_agent_instance_idÚrstripÚ_portal_urlr   Ú_token_leewayÚ	_jwks_urlÚ_authorize_urlÚ
_token_urlÚ_jwks_client)Úselfr   r    r   s   &$$$r&   Ú__init__Ú"NousDashboardAuthProvider.__init__0   s­   € à×#Ñ# H×-Ò-ÜÐ`ÐajÑ`mÐnÓoÐoØ#ŒØ"+¬C°«M¨OÐ"<ˆÔØ%×,Ñ,¨SÓ1ˆÔô *¨,Ó7ˆÔØ ×,Ñ,Ð-Ð-CÐDˆŒØ!%×!1Ñ!1Ð 2Ð2BÐCˆÔØ!×-Ñ-Ð.Ð.>Ð?ˆŒØ!%ˆÖr)   c               ó    € V ^8„  d   QhRRRR/# )r   Úredirect_urir   r"   r   r$   )r%   s   "r&   r'   r(   ?   s   € ÷ yñ y¨3ð y°:ñ yr)   c               	óf   € \        V4       \        V P                  V P                  \        VR 7      # ))r   Úscoper=   )r   r   r6   r/   Ú_SCOPE)r9   r=   s   &$r&   Ústart_loginÚ%NousDashboardAuthProvider.start_login?   s'   € Ü˜lÔ+Ü × 3Ñ 3¸t¿¹ÔV\ÐkwÔxÐxr)   c               ó    € V ^8„  d   QhRRRR/# )r   Úrefresh_tokenr   r"   r#   r$   )r%   s   "r&   r'   r(   C   s   € ÷ ñ ¨sð °tñ r)   c               	ó   € R # ©Nr$   ©r9   rD   s   &$r&   Úrevoke_sessionÚ(NousDashboardAuthProvider.revoke_sessionC   s   € ñ r)   c               ó   € V ^8„  d   QhRR/# )r   r"   r   r$   )r%   s   "r&   r'   r(   J   s   € ÷ ñ ˜3ñ r)   c                	ó   € V P                   # rF   )r5   )r9   s   &r&   Ú	_jwks_uriÚ#NousDashboardAuthProvider._jwks_uriJ   s   € Ø�~‰~Ðr)   c               ó    € V ^8„  d   QhRRRR/# )r   rD   r   r"   z/tuple[dict[str, str], Optional[dict[str, str]]]r$   )r%   s   "r&   r'   r(   M   s   € ÷ 5ñ 5¨cð 5Ð6eñ 5r)   c                	ó.   € R RRV P                   RV/RV/3# )Ú
grant_typerD   r   zx-nous-refresh-token)r/   rG   s   &&r&   Ú_refresh_requestÚ*NousDashboardAuthProvider._refresh_requestM   s+   € ð
 ˜?¨K¸¿¹È/Ð[hÐiØ# ]Ð3ð5ð 	5r)   ÚheadersNÚprevious_refresh_tokenr   c          
     ó,   € V ^8„  d   QhRRRRRRRRR	R
/# )r   Údatazdict[str, str]Úbad_request_excztype[Exception]rS   zOptional[dict[str, str]]rT   r   r"   r   r$   )r%   s   "r&   r'   r(   U   s=   € ÷ 	hñ 	hØ"ð	hØ8Gð	hØRjð	hà #ð	hð 
ñ	hr)   c               	ó”   € \        V P                  WVR RRRR7      w  rVV P                  V\        V4      V P	                  V4      4      # )ÚPortalzPortal token endpointÚaccess_tokenz*Portal token response missing access_token)rS   rW   ÚidpÚendpointÚ	token_keyÚmissing_msg)r   r7   Ú_sessionr   Ú_claims_for)r9   rV   rW   rS   rT   rZ   Úpayloads   &&$$$  r&   Ú_grantÚ NousDashboardAuthProvider._grantU   sO   € ô !/Ø�O‰O˜TÀOØÐ#:ÀnØDô!FÑˆð
 �}‰}˜\Ô+=¸gÓ+FÈ×HXÑHXÐYeÓHfÓgÐgr)   c               ó    € V ^8„  d   QhRRRR/# )r   rZ   r   r"   údict[str, Any]r$   )r%   s   "r&   r'   r(   a   s   € ÷ ñ ¨ð °ñ r)   c           
     	óª  € \        WP                  4       R .V P                  V P                  RV P                  R7      pVP                  R4      pVe.   W0P                  8w  d   \        RV: RV P                  : 24      hVP                  R4      pVf   \        P                  R\        4       V# V\        8w  d   \        RV: R	\         24      hV# )
ÚRS256zaccess token)Ú
algorithmsÚaudienceÚissuerÚlabelÚleewayÚagent_instance_idz"agent_instance_id mismatch: token=z vs configured=Úoauth_contract_versionzjNous Portal token missing oauth_contract_version claim (contract says it should be %d); proceeding anyway.z#unsupported oauth_contract_version=z, expected )r   Ú_get_jwks_clientr/   r3   r4   Úgetr1   r   ÚloggerÚwarningÚ_EXPECTED_CONTRACT_VERSION)r9   rZ   ÚclaimsÚtoken_instance_idÚcontract_versions   &&   r&   r`   Ú%NousDashboardAuthProvider._claims_fora   sæ   € ÜØ×/Ñ/Ó1¸w¸iØ—_‘_Ø×#Ñ#¨>À$×BTÑBTôVˆð #ŸJ™JÐ':Ó;ÐØÒ(Ð->×BYÑBYÔ-YÜØ4Ð5FÑ4IÈÐY]×YpÑYpÑXsÐtóvð và!Ÿ:™:Ð&>Ó?ÐØÒ#Ü�N‰NØ|Ü*ô,ð ˆð Ô!;Ô;ÜØ5Ð6FÑ5IÈÔUoÐTpÐqósð sàˆr)   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rZ   r   rD   rt   re   r"   r   r$   )r%   s   "r&   r'   r(   w   s/   € ÷ ñ  Sð ¸ð Ànð ÐY`ñ r)   c                	óx   € \        V P                  Y1T\        VP                  R 4      ;'       g    R4      R7      # )Úorg_idr   )rZ   rD   rz   )r   Únamer   rp   )r9   rZ   rD   rt   s   &&&&r&   r_   Ú"NousDashboardAuthProvider._sessionw   s:   € ä"Ø�I‰I�vÈÔ^aÐbh×blÑblÐmuÓbv×b|Ðb|Ðz|Ó^}ôð 	r)   )r1   r6   r/   r8   r5   r3   r4   r7   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r{   Údisplay_namer	   r:   rA   rH   rL   rQ   rb   r`   r_   Ú__static_attributes__r$   r)   r&   r   r   *   sZ   † ÙAà€DØ"€Lð&ÐQm÷ &õyõõõ5ð	hØmqð	hà&(÷	hõ÷,ñ r)   r   c               ó   € V ^8„  d   QhRR/# ©r   r"   Údictr$   )r%   s   "r&   r'   r'      s   € ÷ Cñ C Dñ Cr)   c                 ó.   € \        \        \        R R4      # )Ú	dashboardÚoauth)r   rq   Ú_TAGr$   r)   r&   Ú_load_config_oauth_sectionr‹      s   € Üœv¤t¨[¸'ÓBÐBr)   c               ó   € V ^8„  d   QhRR/# r…   r$   )r%   s   "r&   r'   r'   ƒ   s   € ÷ Gñ G�4ñ Gr)   c            
     óZ  € \        4       p \        RV P                  RR4      4      p\        RV P                  RR4      4      ;'       g    \        pV'       g   \	        R4      hVP                  R4      '       g   \	        RV: R	2R
R7      hRVRVR\        V P                  R4      4      /# )z\Resolve NousDashboardAuthProvider kwargs; the skip reason names BOTH configuration surfaces.Ú HERMES_DASHBOARD_OAUTH_CLIENT_IDr   r   ÚHERMES_DASHBOARD_PORTAL_URLr    uy  HERMES_DASHBOARD_OAUTH_CLIENT_ID is not set (and dashboard.oauth.client_id in config.yaml is empty). The Nous Portal provisions this env var (shape 'agent:{instance_id}') when it deploys a Hermes Agent instance â€” set it to your provisioned client id (either as an env var or under dashboard.oauth.client_id in config.yaml), or pass --insecure to skip the OAuth gate entirely.r+   z!HERMES_DASHBOARD_OAUTH_CLIENT_ID=zÀ doesn't match the contract shape 'agent:{instance_id}'. The Nous Portal provisions this value at deploy time; check your Fly app's secrets or override with the value from the Portal admin UI.rr   )Úlevelr   )r‹   r   rp   Ú_DEFAULT_PORTAL_URLr   r-   r   )Úsectionr   r    s      r&   Ú	_settingsr“   ƒ   s½   € ä(Ó*€GÜ"Ð#EÀwÇ{Á{ÐS^Ð`bÓGcÓd€IÜ#Ð$AÀ7Ç;Á;È|Ð]_ÓC`Óa×xÐxÔex€JßÜð#ó$ð 	$ð ×Ñ ×)Ò)ÜØ/°	©}ð =fð gð ô	ð 	ð
 ˜ L°*àœL¨¯©°^Ó)DÓEðGð Gr)   c               ó   € V ^8„  d   QhRR/# )r   r"   r#   r$   )r%   s   "r&   r'   r'   ›   s   € ÷ Oñ O�Tñ Or)   c                ó´   € Rs \        V \        \        \        \
        4      w  ps Ve0   \        P                  RVR,          VR,          VR,          4       R# R# )zFRegister ``NousDashboardAuthProvider`` when a client_id is configured.r   NzTdashboard-auth-nous: registered provider (client_id=%s, portal=%s, token_leeway=%ss)r   r    r   )r   r   rq   rŠ   r   r“   Úinfo)ÚctxÚkwargss   & r&   Úregisterr™   ›   sU   € ð ÐÜ0°´f¼dÔD]Ô_hÓiÑ€FÐØÒÜ�‰ØbØ�;Õ ¨Õ!5°v¸nÕ7Mö	Oñ r)   )(Ú__conditional_annotations__r�   Ú
__future__r   ÚloggingÚtypingr   r   r   Úhermes_cli.dashboard_authr   r   r   Úplugins.dashboard_auth._sharedr	   r
   r   r   r   r   r   r   r   r   r   r   r   Ú	getLoggerr}   rq   rŠ   r‘   r@   rs   r   Ú__annotations__r   r‹   r“   r™   )rš   s   @r&   Ú<module>r¢      sŠ   øðôõ #ã ß &Ñ &ç HÑ H÷÷ ÷ õ ð 
×	Ò	˜8Ó	$€Ø€à7Ð Ø	!€ØÐ àÐ �#Ó ôPÐ 0ô PõjCõG÷0Or)   