--- name: oauth2_refresh kind: function lang: go domain: infra version: "1.0.0" purity: impure signature: "func Oauth2Refresh(config OAuthConfig, refreshToken string) (OAuthTokens, error)" description: "Renueva un access token OAuth2 usando el refresh token. POST al TokenURL con grant_type=refresh_token. Conserva el refresh token original si el proveedor no devuelve uno nuevo." tags: [oauth, oauth2, auth, token, refresh, http, infra, pendiente-usar] uses_functions: [] uses_types: [OAuthConfig_go_infra, OAuthTokens_go_infra] returns: [OAuthTokens_go_infra] returns_optional: false error_type: error_go_core imports: [fmt, net/url] params: - name: config desc: "OAuthConfig del proveedor con ClientID, ClientSecret y TokenURL" - name: refreshToken desc: "refresh token obtenido previamente de Oauth2Exchange" output: "OAuthTokens con nuevo AccessToken. Si el proveedor no devuelve RefreshToken se conserva el original" tested: true tests: ["renueva tokens contra mock server", "conserva refresh token si el proveedor no devuelve uno nuevo", "rechaza refresh vacio"] test_file_path: "functions/infra/oauth2_refresh_test.go" file_path: "functions/infra/oauth2_refresh.go" --- ## Ejemplo ```go tokens, err := Oauth2Refresh(googleConfig, storedRefreshToken) if err != nil { // El refresh token tambien puede haber caducado — forzar relogin HTTPErrorResponse(w, HTTPError{Status: 401, Code: "refresh_failed", Message: err.Error()}) return } saveTokens(tokens) // actualizar tokens en BD/cookie ``` ## Notas Impura — reutiliza oauth2DoTokenRequest para el POST. Algunos proveedores (Google) no devuelven un nuevo RefreshToken al renovar — en ese caso se conserva el original. Otros (Microsoft) pueden rotar el refresh token en cada renovacion: el campo tokens.RefreshToken siempre trae el que hay que guardar para la proxima renovacion. Si el refresh token expiro (el usuario revoco acceso o paso demasiado tiempo) el proveedor retorna 400 con `error: invalid_grant` y se propaga como error.