--- name: oauth2_auth_url kind: function lang: go domain: infra version: "1.0.0" purity: pure signature: "func Oauth2AuthURL(config OAuthConfig, state string) string" description: "Construye la URL de autorizacion OAuth2 a partir de la config. Funcion pura que concatena el AuthURL del proveedor con los query params (client_id, redirect_uri, response_type=code, scope, state)." tags: [oauth, oauth2, auth, url, infra] uses_functions: [] uses_types: [OAuthConfig_go_infra] returns: [] returns_optional: false error_type: "" imports: [net/url, strings] params: - name: config desc: "OAuthConfig del proveedor (Google, GitHub, etc.) con ClientID, AuthURL, RedirectURL y Scopes" - name: state desc: "valor aleatorio anti-CSRF que debe validarse en el callback. Si es vacio no se añade" output: "URL completa a la que redirigir al usuario para iniciar el flujo OAuth2" tested: true tests: ["genera URL con todos los params basicos", "concatena scopes con espacio", "añade state si no es vacio", "detecta si AuthURL ya trae query y usa & en vez de ?"] test_file_path: "functions/infra/oauth2_auth_url_test.go" file_path: "functions/infra/oauth2_auth_url.go" --- ## Ejemplo ```go google := OAuthConfig{ ClientID: os.Getenv("GOOGLE_CLIENT_ID"), AuthURL: "https://accounts.google.com/o/oauth2/v2/auth", RedirectURL: "http://localhost:8080/callback", Scopes: []string{"openid", "email", "profile"}, } state := "random-anti-csrf-token" // guardar en cookie/session url := Oauth2AuthURL(google, state) http.Redirect(w, r, url, http.StatusTemporaryRedirect) ``` ## Notas Pura — solo hace string building con `net/url.Values.Encode()` (ordena params alfabeticamente y hace URL-encoding). No lee env, ni toca I/O, ni `time.Now()`. El state es critico para prevenir CSRF: debe ser aleatorio por sesion, guardarse server-side (cookie firmada, session, etc.) y validarse en el callback antes de hacer Oauth2Exchange. Un state vacio significa sin proteccion CSRF y no se incluye en la URL — solo apto para pruebas locales.