Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for SSO/Oauth #310

Open
JPFrancoia opened this issue Dec 15, 2022 · 0 comments
Open

Support for SSO/Oauth #310

JPFrancoia opened this issue Dec 15, 2022 · 0 comments

Comments

@JPFrancoia
Copy link

JPFrancoia commented Dec 15, 2022

Hi,

This library looks very useful. However at the moment I don't think it supports oauth? Basically I don't want my users to register with a username/password, but instead I'd like them to use SSO, maybe provided by Facebook or Google. Once the oauth flow is complete, I'd like to use my own JWT tokens though. I'd like to use the user info I get from SSO to build the token.

So far I have something like this:

auth.go

func init() {
	// TODO: callback url should be an env var
	facebookProvider := facebook.New(
		os.Getenv("CLIENT_ID"),
		os.Getenv("CLIENT_SECRET"),
		"http://localhost:8080/auth_callback",
	)
	goth.UseProviders(facebookProvider)
}

func Login(c *gin.Context) {
	// TODO: exit if request has valid token?

	// Insert provider into context
	// https://github.com/markbates/goth/issues/411#issuecomment-891037676
	q := c.Request.URL.Query()
	// TODO: make sure :provider is in {'facebook', 'google'},
	// we can't add just any param to the url. Return 400 if not
	q.Add("provider", c.Param("provider"))
	c.Request.URL.RawQuery = q.Encode()

	// try to get the user without re-authenticating
	if user, err := gothic.CompleteUserAuth(c.Writer, c.Request); err == nil {
		logger.Debug("Re-using existing credentials")
		// t, _ := template.New("foo").Parse(userTemplate)
		// t.Execute(c.Writer, user)

		// Return token if we have a valid user
		token, err := createJWT(user.UserID, user.Email)
		if err != nil {
			c.AbortWithError(http.StatusUnauthorized, err)
		}
		c.JSON(http.StatusOK, map[string]string{"token": token})

	} else {
		logger.Debug("Starting auth flow")
		gothic.BeginAuthHandler(c.Writer, c.Request)
	}
}

func AuthCallback(c *gin.Context) {
	user, err := gothic.CompleteUserAuth(c.Writer, c.Request)
	logger.Debug(user)

	if err != nil {
		c.AbortWithError(http.StatusUnauthorized, err)
	}

	// TODO: write user into DB, including provider used

	token, err := createJWT(user.UserID, user.Email)
	if err != nil {
		c.AbortWithError(http.StatusUnauthorized, err)
	}
	c.JSON(http.StatusOK, map[string]string{"token": token})
}

type MyCustomClaims struct {
	Id    string `json:"id"`
	Email string `json:"email"`
	jwt.RegisteredClaims
}

func createJWT(userId string, email string) (string, error) {

	// TODO: in module's init. Make sure this env var is set
	signingKey := []byte(os.Getenv("JWT_SECRET"))

	// Create claims while leaving out some of the optional fields
	claims := MyCustomClaims{
		Id:    userId,
		Email: email,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now()),
			Issuer:    "myapp",
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signedString, err := token.SignedString(signingKey)

	if err != nil {
		return "", err
	}

	return signedString, nil
}

main.go

func main() {

	router := gin.New()

	router.GET("/auth/:provider", auth.Login)
	router.GET("/auth_callback", auth.AuthCallback)
}

I have a HTML page that redirects to auth/{provider} and then the OAuth flow begins. The user is redirected to Facebook, authorizes my app, and then is redirected to my app where they will hit the /auth_callback. That's where I get the user info and I build the token.

This works, but I was wondering if this library supports the 2-steps OAuth process. In the example above, I haven't implemented the refresh token, the logout, or the validation of the token. I'd prefer trusting this library rather than re-implementing that myself, potentially with some security vulnerabilities haha.

If the library can't do that, maybe you heard of another one that could do that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant