This article will teach you how to add Google OAuth Authentication to your Vue.js, MongoDB-Go-Driver, and Golang applications.
Also, after the user is authenticated on the server, we will return JWT access and refresh token cookies to their browser or client.
Related Articles:
- GitHub OAuth Authentication Vue.js and Node.js (No Passport)
- Google OAuth Authentication React.js and Node.js(No Passport)
- GitHub OAuth Authentication React.js and Node.js(No Passport)
- How to Implement GitHub OAuth in React.js
- How to Implement Google OAuth2 in React.js
Prerequisites
Create the Google client ID and secret
Search for Google OAuth in your preferred browser and click on the first link in the search results. You will then be taken to OAuth 2.0 to Access Google APIs.
The OAuth 2.0 to Access Google APIs page contains some instructions on how to obtain the access keys. However, I will guide you step-by-step on how to retrieve the access keys.
On the OAuth 2.0 to Access Google APIs page, click on the Google API Console link to open the Google API console where we will generate the OAuth 2.0 client credentials.
Note: A Google account is required to access the Google API Console page.
Create a New Console Project
Now click on the Google-Oauth dropdown, and a modal should appear where you can either choose an existing project or create a new one.
If you haven’t created any Google API project yet then click on the “New Project” button and type the name of the project. Next, click on the “CREATE” button.
Now grab some coffee and wait a few seconds for Google to set up the project. After the project has been created, a success notification will appear in the top-right corner.
Next, click on the “SELECT PROJECT” button on the notification and tap anywhere on the browser to close the modal.
Set up the OAuth Consent Screen
To start, we need to create the consent screen before we can generate the client ID and secret. The consent screen is where the user will be taken to when they click on the “Sign In with Google” button.
Now select the OAuth consent screen menu under the “APIs & Services” on the sidebar. On the OAuth consent screen, choose the External User Type if it’s a public-facing application you are building and click on the “CREATE” button.
Next, provide the appropriate information needed for the consent screen.
Next, click on the “SAVE AND CONTINUE” button.
On the scopes screen, click on the “ADD OR REMOVE SCOPES” button. Feel free to play around with the available scopes to better understand their use case. However, am only interested in the user’s email and profile information.
Scroll down and click on the “UPDATE” button after selecting the scopes. Next, click on the “SAVE AND CONTINUE” button.
Now click on the ADD USERS button on the Test users screen to add the Google account that will be allowed to sign in to your application whilst in sandbox mode.
Next, click on the “SAVE AND CONTINUE” button after adding the Google account test user.
Now on the Summary screen go through the information you provided and click on the “BACK TO DASHBOARD” button.
Generate the OAuth Client ID and Secret
Under “APIs & Services”, select the Credentials menu and click on “CREATE CREDENTIALS” on the Credentials screen. Next, click on OAuth client ID.
Now provide the required information needed for the OAuth app. The authorized redirect URL should point to a route on your Golang server. Doing it this way will enable us to make the GET request to authorize the user directly from the consent screen.
Next, click on the “CREATE” button to generate the client ID and secret.
Edit the .env
file in the server directory and add the generated client ID, secret, and redirect URL.
.env
GOOGLE_OAUTH_CLIENT_ID=your client Id here
GOOGLE_OAUTH_CLIENT_SECRET=your client secret here
GOOGLE_OAUTH_REDIRECT_URL=http://localhost:8000/api/sessions/oauth/google
In addition, add the credentials to the .env.local
file in the Vue.js application.
VITE_SERVER_ENDPOINT=http://localhost:8000
VITE_GOOGLE_OAUTH_CLIENT_ID=
VITE_GOOGLE_OAUTH_CLIENT_SECRET=
VITE_GOOGLE_OAUTH_REDIRECT=http://localhost:8000/api/sessions/oauth/google
Use
Vue_App_
prefix if you generated the app with the@vue/cli
.
Generate the Consent Screen URL in Vue.js
Now that we have all the configurations in place, we are now ready to add the Google OAuth logic to the Vue.js application.
Next, we need to create a function to generate the OAuth consent screen URL with the client ID, the scopes, and the redirect URL.
src/utils/getGoogleUrl.ts
export const getGoogleUrl = (from) => {
const rootUrl = `https://accounts.google.com/o/oauth2/v2/auth`;
const options = {
redirect_uri: import.meta.env.VITE_GOOGLE_OAUTH_REDIRECT,
client_id: import.meta.env.VITE_GOOGLE_OAUTH_CLIENT_ID,
access_type: 'offline',
response_type: 'code',
prompt: 'consent',
scope: [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email',
].join(' '),
state: from,
};
const qs = new URLSearchParams(options);
return `${rootUrl}?${qs.toString()}`;
};
Build the Google OAuth Button with Vue.js
Next, let’s create a simple login screen with a Google OAuth button having href={getGoogleUrl(from)}
.
src/App.vue
<script setup>
import GoogleLogo from './assets/google.svg';
import { getGoogleUrl } from './utils/getGoogleUrl';
const from = '/';
</script>
<template>
<div class="container">
<div class="social-auth">
<a :href="getGoogleUrl(from)" class="google-auth">
<img :src="GoogleLogo" alt="Google Logo" />
<span>Google</span>
</a>
</div>
</div>
</template>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a {
text-decoration: none;
color: inherit;
}
html {
font-size: 62.5%;
}
body {
font-family: Roboto, sans-serif;
color: #222;
font-size: 1.6rem;
}
.container {
background-color: #2363eb;
height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
}
.social-auth {
max-width: 27rem;
width: 100%;
display: flex;
align-items: center;
flex-direction: column;
}
.google-auth {
background-color: #fff;
border-radius: 5px;
padding: 0.6rem 0;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease-in-out;
}
.google-auth img {
height: 4rem;
margin-right: 1rem;
}
.google-auth span {
font-size: 1.8rem;
}
.google-auth:hover {
box-shadow: 0 1px 13px 0 rgb(0 0 0 / 15%);
}
</style>
On the login screen, click on the Google OAuth button and you should be redirected to the consent screen where you will be asked to select an account from the available logged-in Google accounts.
On the consent screen, click on the test Google account or sign in with the test account if you haven’t already done that.
The Golang server should return a 404 error assuming it is running.
Now take a careful look at the authorized redirect URI and you should see a unique code in the query string.
Later, we’ll use that code to request an access token before making another request with the access token to retrieve the user’s profile information.
The 404 error was returned by the Golang server because we haven’t implemented the Google OAuth logic yet.
Implement the Google OAuth in Golang and MongoDB
Edit the config/default.go
file and add the Google OAuth client ID, the Vue.js origin URL, the client secret, and the authorized callback URL.
You need to add the environment variables to the Config struct for Viper to load and make them available throughout the project.
go get github.com/spf13/viper
config/default.go
type Config struct {
DBUri string `mapstructure:"MONGODB_LOCAL_URI"`
RedisUri string `mapstructure:"REDIS_URL"`
Port string `mapstructure:"PORT"`
ClientOrigin string `mapstructure:"CLIENT_ORIGIN"`
AccessTokenPrivateKey string `mapstructure:"ACCESS_TOKEN_PRIVATE_KEY"`
AccessTokenPublicKey string `mapstructure:"ACCESS_TOKEN_PUBLIC_KEY"`
RefreshTokenPrivateKey string `mapstructure:"REFRESH_TOKEN_PRIVATE_KEY"`
RefreshTokenPublicKey string `mapstructure:"REFRESH_TOKEN_PUBLIC_KEY"`
AccessTokenExpiresIn time.Duration `mapstructure:"ACCESS_TOKEN_EXPIRED_IN"`
RefreshTokenExpiresIn time.Duration `mapstructure:"REFRESH_TOKEN_EXPIRED_IN"`
AccessTokenMaxAge int `mapstructure:"ACCESS_TOKEN_MAXAGE"`
RefreshTokenMaxAge int `mapstructure:"REFRESH_TOKEN_MAXAGE"`
GoogleClientID string `mapstructure:"GOOGLE_OAUTH_CLIENT_ID"`
GoogleClientSecret string `mapstructure:"GOOGLE_OAUTH_CLIENT_SECRET"`
GoogleOAuthRedirectUrl string `mapstructure:"GOOGLE_OAUTH_REDIRECT_URL"`
}
func LoadConfig(path string) (config Config, err error) {
viper.AddConfigPath(path)
viper.SetConfigType("env")
viper.SetConfigName("app")
viper.AutomaticEnv()
err = viper.ReadInConfig()
if err != nil {
return
}
err = viper.Unmarshal(&config)
return
}
Get the Google OAuth Access Token and User’s Profile Data
Now create a utils/googleOAuth.go
file in the root directory and add these two functions:
GetGoogleOauthToken()
– Retrieves the OAuth Access Token from the Google API.GetGoogleUser()
– Exchanges the access token for the user’s profile data.
utils/googleOAuth.go
type GoogleOauthToken struct {
Access_token string
Id_token string
}
type GoogleUserResult struct {
Id string
Email string
Verified_email bool
Name string
Given_name string
Family_name string
Picture string
Locale string
}
func GetGoogleOauthToken(code string) (*GoogleOauthToken, error) {
const rootURl = "https://oauth2.googleapis.com/token"
config, _ := config.LoadConfig(".")
values := url.Values{}
values.Add("grant_type", "authorization_code")
values.Add("code", code)
values.Add("client_id", config.GoogleClientID)
values.Add("client_secret", config.GoogleClientSecret)
values.Add("redirect_uri", config.GoogleOAuthRedirectUrl)
query := values.Encode()
req, err := http.NewRequest("POST", rootURl, bytes.NewBufferString(query))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
client := http.Client{
Timeout: time.Second * 30,
}
res, err := client.Do(req)
if err != nil {
return nil, err
}
if res.StatusCode != http.StatusOK {
return nil, errors.New("could not retrieve token")
}
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
var GoogleOauthTokenRes map[string]interface{}
if err := json.Unmarshal(resBody, &GoogleOauthTokenRes); err != nil {
return nil, err
}
tokenBody := &GoogleOauthToken{
Access_token: GoogleOauthTokenRes["access_token"].(string),
Id_token: GoogleOauthTokenRes["id_token"].(string),
}
return tokenBody, nil
}
func GetGoogleUser(access_token string, id_token string) (*GoogleUserResult, error) {
rootUrl := fmt.Sprintf("https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=%s", access_token)
req, err := http.NewRequest("GET", rootUrl, nil)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", id_token))
client := http.Client{
Timeout: time.Second * 30,
}
res, err := client.Do(req)
if err != nil {
return nil, err
}
if res.StatusCode != http.StatusOK {
return nil, errors.New("could not retrieve user")
}
resBody, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
var GoogleUserRes map[string]interface{}
if err := json.Unmarshal(resBody, &GoogleUserRes); err != nil {
return nil, err
}
userBody := &GoogleUserResult{
Id: GoogleUserRes["id"].(string),
Email: GoogleUserRes["email"].(string),
Verified_email: GoogleUserRes["verified_email"].(bool),
Name: GoogleUserRes["name"].(string),
Given_name: GoogleUserRes["given_name"].(string),
Picture: GoogleUserRes["picture"].(string),
Locale: GoogleUserRes["locale"].(string),
}
return userBody, nil
}
Edit the User Model
If you came from a previous article in this series then update the models/user.model.go
file to have the Photo
and Provider
fields.
Now, when a user registers an account using Google OAuth, we’ll set the provider
field to Google
. However, a normal user who creates the account using email and password will have the provider
to be local
.
models/user.model.ts
type SignUpInput struct {
Name string `json:"name" bson:"name" binding:"required"`
Email string `json:"email" bson:"email" binding:"required"`
Password string `json:"password" bson:"password" binding:"required,min=8"`
PasswordConfirm string `json:"passwordConfirm" bson:"passwordConfirm,omitempty" binding:"required"`
Role string `json:"role" bson:"role"`
Provider string `json:"provider,omitempty" bson:"provider,omitempty"`
Photo string `json:"photo,omitempty" bson:"photo,omitempty"`
Verified bool `json:"verified" bson:"verified"`
CreatedAt time.Time `json:"created_at" bson:"created_at"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
}
type SignInInput struct {
Email string `json:"email" bson:"email" binding:"required"`
Password string `json:"password" bson:"password" binding:"required"`
}
type DBResponse struct {
ID primitive.ObjectID `json:"id" bson:"_id"`
Name string `json:"name" bson:"name"`
Email string `json:"email" bson:"email"`
Password string `json:"password" bson:"password"`
PasswordConfirm string `json:"passwordConfirm,omitempty" bson:"passwordConfirm,omitempty"`
Provider string `json:"provider" bson:"provider"`
Photo string `json:"photo,omitempty" bson:"photo,omitempty"`
Role string `json:"role" bson:"role"`
Verified bool `json:"verified" bson:"verified"`
CreatedAt time.Time `json:"created_at" bson:"created_at"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
}
type UserResponse struct {
ID primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
Email string `json:"email,omitempty" bson:"email,omitempty"`
Role string `json:"role,omitempty" bson:"role,omitempty"`
Photo string `json:"photo,omitempty" bson:"photo,omitempty"`
Provider string `json:"provider" bson:"provider"`
CreatedAt time.Time `json:"created_at" bson:"created_at"`
UpdatedAt time.Time `json:"updated_at" bson:"updated_at"`
}
type UpdateDBUser struct {
ID primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
Email string `json:"email,omitempty" bson:"email,omitempty"`
Password string `json:"password,omitempty" bson:"password,omitempty"`
PasswordConfirm string `json:"passwordConfirm,omitempty" bson:"passwordConfirm,omitempty"`
Role string `json:"role,omitempty" bson:"role,omitempty"`
Provider string `json:"provider" bson:"provider"`
Photo string `json:"photo,omitempty" bson:"photo,omitempty"`
Verified bool `json:"verified,omitempty" bson:"verified,omitempty"`
CreatedAt time.Time `json:"created_at,omitempty" bson:"created_at,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty" bson:"updated_at,omitempty"`
}
func FilteredResponse(user *DBResponse) UserResponse {
return UserResponse{
ID: user.ID,
Email: user.Email,
Name: user.Name,
Role: user.Role,
Provider: user.Provider,
Photo: user.Photo,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}
}
Include a Service to Upsert the User
Let’s define a utility function to marshal and unmarshal the struct into a BSON document.
utils/helper.go
func ToDoc(v interface{}) (doc *bson.D, err error) {
data, err := bson.Marshal(v)
if err != nil {
return
}
err = bson.Unmarshal(data, &doc)
return
}
Now let’s add an UpsertUser
method to the UserService
interface. The UpsertUser
function will be responsible for accessing and mutating the MongoDB database.
services/user.service.go
type UserService interface {
FindUserById(string) (*models.DBResponse, error)
FindUserByEmail(string) (*models.DBResponse, error)
UpsertUser(string, *models.UpdateDBUser) (*models.DBResponse, error)
}
Next, add the code below to the services/user.service.impl.go
file. The UpsertUser
method will enable us to upsert the user’s credentials in the MongoDB database.
services/user.service.impl.go
type UserServiceImpl struct {
collection *mongo.Collection
ctx context.Context
}
func NewUserServiceImpl(collection *mongo.Collection, ctx context.Context) UserService {
return &UserServiceImpl{collection, ctx}
}
// FindUserByID
// FindUserByEmail
// UpsertUser
func (uc *UserServiceImpl) UpsertUser(email string, data *models.UpdateDBUser) (*models.DBResponse, error) {
doc, err := utils.ToDoc(data)
if err != nil {
return nil, err
}
opts := options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(1)
query := bson.D{{Key: "email", Value: email}}
update := bson.D{{Key: "$set", Value: doc}}
res := uc.collection.FindOneAndUpdate(uc.ctx, query, update, opts)
var updatedPost *models.DBResponse
if err := res.Decode(&updatedPost); err != nil {
return nil, errors.New("no post with that Id exists")
}
return updatedPost, nil
}
In the above code snippets, you’ll notice that we used .SetUpsert(true)
on the FindOneAndUpdateOptions instance returned by evoking options.FindOneAndUpdate()
.
Adding .SetUpsert(true)
will inform MongoDB to create a new user document if the email used in the BSON query does not exist in the database. However, MongoDB will only update the user document if that email already exists.
Define the Google OAuth Controller
Now let’s install the Golang JWT package to enable us to generate the JSON Web Tokens.
At the time of writing this article, the current version is v4 but this might change in the future so visit their GitHub page to download the recent version.
go get -u github.com/golang-jwt/jwt/v4
Next, create these two functions to sign and verify the access and refresh tokens
utils/token.go
func CreateToken(ttl time.Duration, payload interface{}, privateKey string) (string, error) {
decodedPrivateKey, err := base64.StdEncoding.DecodeString(privateKey)
if err != nil {
return "", fmt.Errorf("could not decode key: %w", err)
}
key, err := jwt.ParseRSAPrivateKeyFromPEM(decodedPrivateKey)
if err != nil {
return "", fmt.Errorf("create: parse key: %w", err)
}
now := time.Now().UTC()
claims := make(jwt.MapClaims)
claims["sub"] = payload
claims["exp"] = now.Add(ttl).Unix()
claims["iat"] = now.Unix()
claims["nbf"] = now.Unix()
token, err := jwt.NewWithClaims(jwt.SigningMethodRS256, claims).SignedString(key)
if err != nil {
return "", fmt.Errorf("create: sign token: %w", err)
}
return token, nil
}
func ValidateToken(token string, publicKey string) (interface{}, error) {
decodedPublicKey, err := base64.StdEncoding.DecodeString(publicKey)
if err != nil {
return nil, fmt.Errorf("could not decode: %w", err)
}
key, err := jwt.ParseRSAPublicKeyFromPEM(decodedPublicKey)
if err != nil {
return "", fmt.Errorf("validate: parse key: %w", err)
}
parsedToken, err := jwt.Parse(token, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
return nil, fmt.Errorf("unexpected method: %s", t.Header["alg"])
}
return key, nil
})
if err != nil {
return nil, fmt.Errorf("validate: %w", err)
}
claims, ok := parsedToken.Claims.(jwt.MapClaims)
if !ok || !parsedToken.Valid {
return nil, fmt.Errorf("validate: invalid token")
}
return claims["sub"], nil
}
Now let’s include the GoogleOAuth
handler in the controllers/auth.controller.go
file. This handler will be evoked when Google redirects the user to the server.
controllers/auth.controller.ts
type AuthController struct {
authService services.AuthService
userService services.UserService
}
func NewAuthController(authService services.AuthService, userService services.UserService) AuthController {
return AuthController{authService, userService}
}
// SignUp User
// SignIn User
// Refresh Access Token
func (ac *AuthController) RefreshAccessToken(ctx *gin.Context) {
message := "could not refresh access token"
cookie, err := ctx.Cookie("refresh_token")
if err != nil {
ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"status": "fail", "message": message})
return
}
config, _ := config.LoadConfig(".")
sub, err := utils.ValidateToken(cookie, config.RefreshTokenPublicKey)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"status": "fail", "message": err.Error()})
return
}
user, err := ac.userService.FindUserById(fmt.Sprint(sub))
if err != nil {
ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"status": "fail", "message": "the user belonging to this token no logger exists"})
return
}
access_token, err := utils.CreateToken(config.AccessTokenExpiresIn, user.ID, config.AccessTokenPrivateKey)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"status": "fail", "message": err.Error()})
return
}
ctx.SetCookie("access_token", access_token, config.AccessTokenMaxAge*60, "/", "localhost", false, true)
ctx.SetCookie("logged_in", "true", config.AccessTokenMaxAge*60, "/", "localhost", false, false)
ctx.JSON(http.StatusOK, gin.H{"status": "success", "access_token": access_token})
}
func (ac *AuthController) GoogleOAuth(ctx *gin.Context) {
code := ctx.Query("code")
var pathUrl string = "/"
if ctx.Query("state") != "" {
pathUrl = ctx.Query("state")
}
if code == "" {
ctx.JSON(http.StatusUnauthorized, gin.H{"status": "fail", "message": "Authorization code not provided!"})
return
}
// Use the code to get the id and access tokens
tokenRes, err := utils.GetGoogleOauthToken(code)
if err != nil {
ctx.JSON(http.StatusBadGateway, gin.H{"status": "fail", "message": err.Error()})
}
user, err := utils.GetGoogleUser(tokenRes.Access_token, tokenRes.Id_token)
if err != nil {
ctx.JSON(http.StatusBadGateway, gin.H{"status": "fail", "message": err.Error()})
}
createdAt := time.Now()
resBody := &models.UpdateDBUser{
Email: user.Email,
Name: user.Name,
Photo: user.Picture,
Provider: "google",
Role: "user",
Verified: true,
CreatedAt: createdAt,
UpdatedAt: createdAt,
}
updatedUser, err := ac.userService.UpsertUser(user.Email, resBody)
if err != nil {
ctx.JSON(http.StatusBadGateway, gin.H{"status": "fail", "message": err.Error()})
}
config, _ := config.LoadConfig(".")
// Generate Tokens
access_token, err := utils.CreateToken(config.AccessTokenExpiresIn, updatedUser.ID.Hex(), config.AccessTokenPrivateKey)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"status": "fail", "message": err.Error()})
return
}
refresh_token, err := utils.CreateToken(config.RefreshTokenExpiresIn, updatedUser.ID.Hex(), config.RefreshTokenPrivateKey)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"status": "fail", "message": err.Error()})
return
}
ctx.SetCookie("access_token", access_token, config.AccessTokenMaxAge*60, "/", "localhost", false, true)
ctx.SetCookie("refresh_token", refresh_token, config.RefreshTokenMaxAge*60, "/", "localhost", false, true)
ctx.SetCookie("logged_in", "true", config.AccessTokenMaxAge*60, "/", "localhost", false, false)
ctx.Redirect(http.StatusTemporaryRedirect, fmt.Sprint(config.ClientOrigin, pathUrl))
}
Define the Route
Next, create a routes/session.routes.go
file and add the code snippets below.
routes/session.routes.go
type SessionRouteController struct {
authController controllers.AuthController
}
func NewSessionRouteController(authController controllers.AuthController) SessionRouteController {
return SessionRouteController{authController}
}
func (rc *SessionRouteController) SessionRoute(rg *gin.RouterGroup) {
router := rg.Group("/sessions/oauth")
router.GET("/google", rc.authController.GoogleOAuth)
}
We created a new route file because we will later implement GitHub and Facebook OAuth and it makes sense to group them in a single route file.
Register the Session Router
Next, let’s add the router we created above to the Gin Gonic middleware stack in the main.go
file. Also, we need to install the cors package to enable us to accept requests from the Vue.js app.
var (
server *gin.Engine
ctx context.Context
mongoclient *mongo.Client
redisclient *redis.Client
userService services.UserService
UserController controllers.UserController
UserRouteController routes.UserRouteController
authCollection *mongo.Collection
authService services.AuthService
AuthController controllers.AuthController
AuthRouteController routes.AuthRouteController
SessionRouteController routes.SessionRouteController
)
func init() {
config, err := config.LoadConfig(".")
if err != nil {
log.Fatal("Could not load environment variables", err)
}
ctx = context.TODO()
// Connect to MongoDB
mongoconn := options.Client().ApplyURI(config.DBUri)
mongoclient, err := mongo.Connect(ctx, mongoconn)
if err != nil {
panic(err)
}
if err := mongoclient.Ping(ctx, readpref.Primary()); err != nil {
panic(err)
}
fmt.Println("MongoDB successfully connected...")
// Connect to Redis
redisclient = redis.NewClient(&redis.Options{
Addr: config.RedisUri,
})
if _, err := redisclient.Ping(ctx).Result(); err != nil {
panic(err)
}
err = redisclient.Set(ctx, "test", "Welcome to Golang with Redis and MongoDB", 0).Err()
if err != nil {
panic(err)
}
fmt.Println("Redis client connected successfully...")
// Collections
authCollection = mongoclient.Database("golang_mongodb").Collection("users")
userService = services.NewUserServiceImpl(authCollection, ctx)
authService = services.NewAuthService(authCollection, ctx)
AuthController = controllers.NewAuthController(authService, userService)
AuthRouteController = routes.NewAuthRouteController(AuthController)
SessionRouteController = routes.NewSessionRouteController(AuthController)
UserController = controllers.NewUserController(userService)
UserRouteController = routes.NewRouteUserController(UserController)
server = gin.Default()
}
func main() {
config, err := config.LoadConfig(".")
if err != nil {
log.Fatal("Could not load config", err)
}
defer mongoclient.Disconnect(ctx)
value, err := redisclient.Get(ctx, "test").Result()
if err == redis.Nil {
fmt.Println("key: test does not exist")
} else if err != nil {
panic(err)
}
corsConfig := cors.DefaultConfig()
corsConfig.AllowOrigins = []string{"http://localhost:8000", "http://localhost:3000"}
corsConfig.AllowCredentials = true
server.Use(cors.New(corsConfig))
router := server.Group("/api")
router.GET("/healthchecker", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{"status": "success", "message": value})
})
AuthRouteController.AuthRoute(router)
UserRouteController.UserRoute(router, userService)
SessionRouteController.SessionRoute(router)
log.Fatal(server.Run(":" + config.Port))
}
Conclusion
Congratulations on reaching the end. In this article, you learned how to add Google OAuth Authentication to your Vue.js, Golang, Gin Gonic, and MongoDB applications.
Check out the source code here: