Merge pull request #21 from fossyy/staging

Handle Google OAuth2 error on callback
This commit is contained in:
2024-05-07 20:29:57 +07:00
committed by GitHub
7 changed files with 88 additions and 12 deletions

View File

@ -31,6 +31,7 @@ const (
type Database interface { type Database interface {
IsUserRegistered(email string, username string) bool IsUserRegistered(email string, username string) bool
IsEmailRegistered(email string) bool
CreateUser(user *models.User) error CreateUser(user *models.User) error
GetUser(email string) (*models.User, error) GetUser(email string) (*models.User, error)
@ -149,6 +150,18 @@ func (db *mySQLdb) IsUserRegistered(email string, username string) bool {
return true return true
} }
func (db *mySQLdb) IsEmailRegistered(email string) bool {
var data models.User
err := db.DB.Table("users").Where("email = ? ", email).First(&data).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return false
}
return true
}
return true
}
func (db *mySQLdb) CreateUser(user *models.User) error { func (db *mySQLdb) CreateUser(user *models.User) error {
err := db.DB.Create(user).Error err := db.DB.Create(user).Error
if err != nil { if err != nil {
@ -240,6 +253,18 @@ func (db *postgresDB) IsUserRegistered(email string, username string) bool {
return true return true
} }
func (db *postgresDB) IsEmailRegistered(email string) bool {
var data models.User
err := db.DB.Table("users").Where("email = $1 ", email).First(&data).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return false
}
return true
}
return true
}
func (db *postgresDB) CreateUser(user *models.User) error { func (db *postgresDB) CreateUser(user *models.User) error {
err := db.DB.Create(user).Error err := db.DB.Create(user).Error
if err != nil { if err != nil {

View File

@ -91,6 +91,11 @@ func GET(w http.ResponseWriter, r *http.Request) {
delete(CsrfTokens, r.URL.Query().Get("state")) delete(CsrfTokens, r.URL.Query().Get("state"))
if err := r.URL.Query().Get("error"); err != "" {
http.Redirect(w, r, fmt.Sprintf("/signin?error=%s", err), http.StatusFound)
return
}
formData := url.Values{ formData := url.Values{
"grant_type": {"authorization_code"}, "grant_type": {"authorization_code"},
"code": {r.URL.Query().Get("code")}, "code": {r.URL.Query().Get("code")},
@ -98,6 +103,7 @@ func GET(w http.ResponseWriter, r *http.Request) {
"client_secret": {utils.Getenv("GOOGLE_CLIENT_SECRET")}, "client_secret": {utils.Getenv("GOOGLE_CLIENT_SECRET")},
"redirect_uri": {utils.Getenv("GOOGLE_CALLBACK")}, "redirect_uri": {utils.Getenv("GOOGLE_CALLBACK")},
} }
resp, err := http.Post("https://oauth2.googleapis.com/token", "application/x-www-form-urlencoded", bytes.NewBufferString(formData.Encode())) resp, err := http.Post("https://oauth2.googleapis.com/token", "application/x-www-form-urlencoded", bytes.NewBufferString(formData.Encode()))
if err != nil { if err != nil {
log.Error("Error:", err) log.Error("Error:", err)
@ -135,11 +141,15 @@ func GET(w http.ResponseWriter, r *http.Request) {
response, err := http.Post("https://oauth2.googleapis.com/revoke", "application/json", bytes.NewBuffer(requestBody)) response, err := http.Post("https://oauth2.googleapis.com/revoke", "application/json", bytes.NewBuffer(requestBody))
if err != nil { if err != nil {
log.Error("Error revoking access token: ", err) log.Error("Error revoking access token: ", err)
http.Error(w, "Failed to revoke access token", http.StatusInternalServerError)
return
} }
defer response.Body.Close() defer response.Body.Close()
if response.StatusCode != http.StatusOK { if response.StatusCode != http.StatusOK {
log.Error("Error revoking access token: ", response.StatusCode) log.Error("Error revoking access token: ", response.StatusCode)
http.Error(w, "Failed to revoke access token", http.StatusInternalServerError)
return
} }
var oauthUser OauthUser var oauthUser OauthUser
@ -149,7 +159,13 @@ func GET(w http.ResponseWriter, r *http.Request) {
return return
} }
if !db.DB.IsUserRegistered(oauthUser.Email, "ll") { if oauthUser.Email == "" {
log.Error("Error reading user info response body: email not found")
http.Error(w, "Invalid email is given", http.StatusInternalServerError)
return
}
if !db.DB.IsEmailRegistered(oauthUser.Email) {
code := utils.GenerateRandomString(64) code := utils.GenerateRandomString(64)
googleOauthSetupHandler.SetupUser[code] = &googleOauthSetupHandler.UnregisteredUser{ googleOauthSetupHandler.SetupUser[code] = &googleOauthSetupHandler.UnregisteredUser{
Code: code, Code: code,
@ -167,6 +183,7 @@ func GET(w http.ResponseWriter, r *http.Request) {
log.Error(err.Error()) log.Error(err.Error())
return return
} }
storeSession := session.Create() storeSession := session.Create()
storeSession.Values["user"] = types.User{ storeSession.Values["user"] = types.User{
UserID: user.UserID, UserID: user.UserID,

View File

@ -2,6 +2,7 @@ package signinHandler
import ( import (
"errors" "errors"
"github.com/a-h/templ"
"github.com/fossyy/filekeeper/cache" "github.com/fossyy/filekeeper/cache"
"net/http" "net/http"
"strings" "strings"
@ -14,16 +15,49 @@ import (
) )
var log *logger.AggregatedLogger var log *logger.AggregatedLogger
var errorMessages = make(map[string]string)
func init() { func init() {
errorMessages = map[string]string{
"redirect_uri_mismatch": "The redirect URI provided does not match the one registered with our service. Please contact the administrator for assistance.",
"invalid_request": "The request is missing required parameters or has invalid values. Please try again or contact support for assistance.",
"access_denied": "Access was denied. You may have declined the request for permission. Please try again if you wish to grant access.",
"unauthorized_client": "You are not authorized to make this request. Please contact support for further assistance.",
"unsupported_response_type": "The requested response type is not supported. Please try again with a supported response type.",
"invalid_scope": "The requested scope is invalid or unknown. Please try again or contact support for assistance.",
"server_error": "Our server encountered an unexpected error. Please try again later or contact support for assistance.",
"temporarily_unavailable": "Our server is currently undergoing maintenance. Please try again later.",
"invalid_grant": "The authorization code or refresh token provided is invalid. Please try again or contact support for assistance.",
"invalid_client": "The client identifier provided is invalid. Please check your credentials and try again.",
"invalid_token": "The access token provided is invalid. Please try again or contact support for assistance.",
"insufficient_scope": "You do not have sufficient privileges to perform this action. Please contact support for assistance.",
"interaction_required": "Interaction with the authorization server is required. Please try again.",
"login_required": "You need to log in again to proceed. Please try logging in again.",
"account_selection_required": "Please select an account to proceed with the request.",
"consent_required": "Consent is required to proceed. Please provide consent to continue.",
}
log = logger.Logger() log = logger.Logger()
} }
func GET(w http.ResponseWriter, r *http.Request) { func GET(w http.ResponseWriter, r *http.Request) {
component := signinView.Main("Sign in Page", types.Message{ var component templ.Component
if err := r.URL.Query().Get("error"); err != "" {
message, ok := errorMessages[err]
if !ok {
message = "Unknown error occurred. Please contact support at bagas@fossy.my.id for assistance."
}
component = signinView.Main("Sign in Page", types.Message{
Code: 0,
Message: message,
})
} else {
component = signinView.Main("Sign in Page", types.Message{
Code: 3, Code: 3,
Message: "", Message: "",
}) })
}
err := component.Render(r.Context(), w) err := component.Render(r.Context(), w)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

View File

@ -16,11 +16,11 @@ templ form(err types.Message, title string) {
switch err.Code { switch err.Code {
case 0: case 0:
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert"> <div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
<span class="font-medium">Danger alert!</span> {err.Message} {err.Message}
</div> </div>
case 1: case 1:
<div class="p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400" role="alert"> <div class="p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400" role="alert">
<span class="font-medium">Success alert!</span> {err.Message} {err.Message}
</div> </div>
} }
</div> </div>

View File

@ -16,7 +16,7 @@ templ content(title string, err types.Message) {
switch err.Code { switch err.Code {
case 0: case 0:
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert"> <div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
<span class="font-medium">Danger alert!</span> {err.Message} {err.Message}
</div> </div>
} }
</div> </div>
@ -50,7 +50,7 @@ templ NewPasswordForm(title string, err types.Message) {
switch err.Code { switch err.Code {
case 0: case 0:
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert"> <div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
<span class="font-medium">Danger alert!</span> {err.Message} {err.Message}
</div> </div>
} }
</div> </div>

View File

@ -16,7 +16,7 @@ templ content(err types.Message, title string) {
switch err.Code { switch err.Code {
case 0: case 0:
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert"> <div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
<span class="font-medium">Danger alert!</span> {err.Message} {err.Message}
</div> </div>
} }
</div> </div>

View File

@ -16,11 +16,11 @@ templ form(err types.Message, title string) {
switch err.Code { switch err.Code {
case 0: case 0:
<div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert"> <div class="p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
<span class="font-medium">Danger alert!</span> {err.Message} {err.Message}
</div> </div>
case 1: case 1:
<div class="p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400" role="alert"> <div class="p-4 mb-4 text-sm text-green-800 rounded-lg bg-green-50 dark:bg-gray-800 dark:text-green-400" role="alert">
<span class="font-medium">Success alert!</span> {err.Message} {err.Message}
</div> </div>
} }
</div> </div>