goth icon indicating copy to clipboard operation
goth copied to clipboard

this is more of a question...

Open tobyweb3x opened this issue 10 months ago • 4 comments

is it possible to have two callback URL in a single we app, adding more context would better explain me. So, my application has two types of user models (accounts [the usual "users" ] & agents) with diff. UI but running on the same backend. The endpoint would look like this;

mysite.com/account/google/callback

&

mysite.com/agent/google/callback

for the same provider (google).

i tried adding the two callback here but it not going through even after i included the second on the Google OUATH2 API console. Is this a limitation of the gothic (i read somehow it simpleton, so no two instance can exist -if that can be a solution though) Or this would'nt be possible by the spec itself? thanks.

	gothic.Store = store
	goth.UseProviders(
		google.New(cfg.LoginProvider.Google.ClientKey, cfg.LoginProvider.Google.Secret, callbackURL),
	)

tobyweb3x avatar Mar 08 '25 06:03 tobyweb3x

Another solution i'm thinking is to keep state of a data between the request from the gothic.BeginAuthHandler(w, r) func to the callback func, i extract this state and direct the user to the specified view... but no ideaa how do do that (if it possible), i think the request is cleaned somehow.

something like this

func (a *App) signInWithProvider(w http.ResponseWriter, r *http.Request, userType string) {
	var provider string
	if provider = chi.URLParam(r, "provider"); provider == "" {
		http.Redirect(w, r, fmt.Sprintf("/%s/login.html", userType), http.StatusTemporaryRedirect)
		return
	}

	if provider != "google" && provider != "facebook" {
		http.Redirect(w, r, fmt.Sprintf("/%s/login.html", userType), http.StatusTemporaryRedirect)
		return
	}

	q := r.URL.Query()
	q.Add("provider", provider)
	q.Add("user_type", userType)
	r.URL.RawQuery = q.Encode()

	// fmt.Printf("\n\n------%+v\n\n", *r)

	gothic.BeginAuthHandler(w, r)
}

then in the callback func;

func (a *App) callbackHandler(w http.ResponseWriter, r *http.Request) {

	// fmt.Printf("\n\n%+v\n\n", *r)
	userType := r.URL.Query().Get("user_type")
	fmt.Println("User Type from Query:", userType)

	user, err := gothic.CompleteUserAuth(w, r)
	if err != nil {
		http.Redirect(w, r, fmt.Sprintf("/%s/login.html", userType), http.StatusTemporaryRedirect)
		return
	}

	if userType == "agent" {
		if err = a.logInAgent(w, r, user); err != nil {
			http.Redirect(w, r, "/agent/login.html", http.StatusTemporaryRedirect)
			return
		}

		return
	}

	if err = a.logInAccount(w, r, user); err != nil {
		http.Redirect(w, r, "/account/login.html", http.StatusTemporaryRedirect)
		return
	}
}

but the field was missing in the request in the callback handler func

tobyweb3x avatar Mar 10 '25 13:03 tobyweb3x

Just in case you're still looking at this. Indeed, I tried and can't get query params to work. So this is possibly not what you would want to do. But it works if I just add a cookie. The gothic.Store should have http.SameSiteLaxMode but you can probably use any other cookie that has the same Let's say account/auth/google and agent/auth/google. In those I tried:

session, err := gothic.Store.Get(r, "source_cookie")
if err != nil {
    ...
}
session.Values['source'] = "agent"
err = session.Save(r, w)
if err != nil {
    ...
}

Then just add account as source for the account route. Then I only have one /auth/:provider/callback and I just check what my source_cookie cookie has as the source in the callback.

session, err := gothic.Store.Get(r, "source_cookie")
if err != nil {
    ...
}
source, source_ok := session.Values['source'].(string)
if !source_ok {
    ...
}
if source == "agent" {
	if err = a.logInAgent(w, r, user); err != nil {
		http.Redirect(w, r, "/agent/login.html", http.StatusTemporaryRedirect)
		return
	}

	return
}
...

bboerkoel avatar Apr 09 '25 09:04 bboerkoel

thanks for taking the time to compose the response @bboerkoel, i already found a solution, so it possible to keep state from gothic.BeginAuthHandler(w, r) to the func that handles your callback url.

there is this state param you can inlcude, like this

q := r.URL.Query()
	q.Add("provider", provider)
	q.Add("state", userType)

and in my callback func, i can do this

func (a *App) callbackHandler(w http.ResponseWriter, r *http.Request) {

	var (
		userType = r.URL.Query().Get("state")
....

tobyweb3x avatar Apr 10 '25 13:04 tobyweb3x

Happy to try to help. Ah thanks, I guess it makes sense something like that is possible, thanks. I'll keep that in mind if I ever need it 👍

bboerkoel avatar Apr 10 '25 14:04 bboerkoel