brokeyourbike/gin-firebase-middleware

View on GitHub
middleware.go

Summary

Maintainability
A
0 mins
Test Coverage
A
91%
package ginfirebasemw

import (
    "bytes"
    "encoding/base64"
    "encoding/json"
    "net/http"
    "strings"

    "github.com/gin-gonic/gin"
    "github.com/gin-gonic/gin/binding"
)

// gatewayUserInfoHeader is the header that contains the user info.
const gatewayUserInfoHeader = "X-Apigateway-Api-Userinfo"

// userInfoCtx is a context key for the UserInfo.
const userInfoCtx = "FirebaseApiGatewayUserInfo"

const ProviderPassword = "password"
const ProviderApple = "apple.com"
const SecondFactorPhone = "phone"

type UserInfo struct {
    Name          string `json:"name"`
    Sub           string `json:"sub" binding:"required"`
    Email         string `json:"email" binding:"omitempty,email"`
    EmailVerified bool   `json:"email_verified"`
    Firebase      struct {
        SignInProvider     string `json:"sign_in_provider"`
        SignInSecondFactor string `json:"sign_in_second_factor"`
    } `json:"firebase"`
}

func (u UserInfo) IsServiceAccount() bool {
    return strings.HasSuffix(u.Email, "gserviceaccount.com")
}

func Middleware() gin.HandlerFunc {
    return func(ctx *gin.Context) {
        encodedUser := ctx.GetHeader(gatewayUserInfoHeader)
        if encodedUser == "" {
            ctx.AbortWithStatus(http.StatusForbidden)
            return
        }

        decodedBytes, err := base64.RawURLEncoding.DecodeString(encodedUser)
        if err != nil {
            ctx.AbortWithStatus(http.StatusForbidden)
            return
        }

        var userInfo UserInfo

        if err := json.NewDecoder(bytes.NewReader(decodedBytes)).Decode(&userInfo); err != nil {
            ctx.AbortWithStatus(http.StatusForbidden)
            return
        }

        if err := binding.Validator.ValidateStruct(&userInfo); err != nil {
            ctx.AbortWithStatus(http.StatusForbidden)
            return
        }

        ctx.Set(userInfoCtx, userInfo)
        ctx.Next()
    }
}

// GetUserInfo returns the firebase user info from the context.
func GetUserInfo(ctx *gin.Context) UserInfo {
    userInfo := ctx.MustGet(userInfoCtx).(UserInfo)
    return userInfo
}

// GetUserID returns the firebase user ID from the context.
func GetUserID(ctx *gin.Context) string {
    userInfo := GetUserInfo(ctx)
    return userInfo.Sub
}