Redirect to external login form

This commit is contained in:
nemunaire 2021-12-16 19:21:30 +01:00
parent 4f1b20e392
commit 15fea62a0c
6 changed files with 58 additions and 8 deletions

View File

@ -127,6 +127,18 @@ func retrieveSessionFromClaims(claims *UserClaims, user *happydns.User, session_
return
}
func requireLogin(opts *config.Options, c *gin.Context, msg string) {
if opts.ExternalAuth.URL != nil {
customurl := *opts.ExternalAuth.URL
q := customurl.Query()
q.Set("errmsg", msg)
customurl.RawQuery = q.Encode()
c.Header("Location", customurl.String())
}
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": msg})
}
func authMiddleware(opts *config.Options, optional bool) gin.HandlerFunc {
return func(c *gin.Context) {
var token string
@ -143,7 +155,7 @@ func authMiddleware(opts *config.Options, optional bool) gin.HandlerFunc {
if optional {
c.Next()
} else {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": "No authorization token found in cookie nor in Authorization header."})
requireLogin(opts, c, "No authorization token found in cookie nor in Authorization header.")
}
return
}
@ -157,7 +169,7 @@ func authMiddleware(opts *config.Options, optional bool) gin.HandlerFunc {
if err != nil {
log.Printf("%s provide a bad JWT claims: %s", c.ClientIP(), err.Error())
c.SetCookie(COOKIE_NAME, "", -1, opts.BaseURL+"/", "", opts.DevProxy == "", true)
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": fmt.Sprintf("Something goes wrong with your session. Please reconnect.")})
requireLogin(opts, c, "Something goes wrong with your session. Please reconnect.")
return
}
@ -165,14 +177,14 @@ func authMiddleware(opts *config.Options, optional bool) gin.HandlerFunc {
if len(claims.Profile.UserId) == 0 {
log.Printf("%s: no UserId found in JWT claims", c.ClientIP())
c.SetCookie(COOKIE_NAME, "", -1, opts.BaseURL+"/", "", opts.DevProxy == "", true)
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": fmt.Sprintf("Something goes wrong with your session. Please reconnect.")})
requireLogin(opts, c, "Something goes wrong with your session. Please reconnect.")
return
}
if claims.Profile.Email == "" {
log.Printf("%s: no Email found in JWT claims", c.ClientIP())
c.SetCookie(COOKIE_NAME, "", -1, opts.BaseURL+"/", "", opts.DevProxy == "", true)
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": fmt.Sprintf("Something goes wrong with your session. Please reconnect.")})
requireLogin(opts, c, "Something goes wrong with your session. Please reconnect.")
return
}
@ -181,7 +193,7 @@ func authMiddleware(opts *config.Options, optional bool) gin.HandlerFunc {
if err != nil {
log.Printf("%s %s", c.ClientIP(), err.Error())
c.SetCookie(COOKIE_NAME, "", -1, opts.BaseURL+"/", "", opts.DevProxy == "", true)
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": fmt.Sprintf("Something goes wrong with your session. Please reconnect.")})
requireLogin(opts, c, "Something goes wrong with your session. Please reconnect.")
return
}
@ -193,7 +205,7 @@ func authMiddleware(opts *config.Options, optional bool) gin.HandlerFunc {
if err != nil {
log.Printf("%s %s", c.ClientIP(), err.Error())
c.SetCookie(COOKIE_NAME, "", -1, opts.BaseURL+"/", "", opts.DevProxy == "", true)
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": fmt.Sprintf("Your session has expired. Please reconnect.")})
requireLogin(opts, c, "Your session has expired. Please reconnect.")
return
}

View File

@ -94,7 +94,7 @@ func displayAuthToken(c *gin.Context) {
func displayNotAuthToken(opts *config.Options, c *gin.Context) {
if !opts.NoAuth {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"errmsg": "Authorization required"})
requireLogin(opts, c, "Authorization required")
return
}

View File

@ -49,6 +49,7 @@ func (o *Options) declareFlags() {
flag.Var(&o.StorageEngine, "storage-engine", fmt.Sprintf("Select the storage engine between %v", storage.GetStorageEngines()))
flag.BoolVar(&o.NoAuth, "no-auth", false, "Disable user access control, use default account")
flag.Var(&o.JWTSecretKey, "jwt-secret-key", "Secret key used to verify JWT authentication tokens (a random secret is used if undefined)")
flag.Var(&o.ExternalAuth, "external-auth", "Base URL to use for login and registration (use embedded forms if left empty)")
// Others flags are declared in some other files likes sources, storages, ... when they need specials configurations
}

View File

@ -71,6 +71,9 @@ type Options struct {
// NoAuth controls if there is user access control or not.
NoAuth bool
// ExternalAuth is the URL of the login form to use instead of the embedded one.
ExternalAuth URL
// JWTSecretKey stores the private key to sign and verify JWT tokens.
JWTSecretKey JWTSecretKey
}

View File

@ -33,6 +33,7 @@ package config // import "happydns.org/config"
import (
"encoding/base64"
"net/url"
)
type JWTSecretKey []byte
@ -50,3 +51,25 @@ func (i *JWTSecretKey) Set(value string) error {
*i = z
return nil
}
type URL struct {
URL *url.URL
}
func (i *URL) String() string {
if i.URL != nil {
return i.URL.String()
} else {
return ""
}
}
func (i *URL) Set(value string) error {
u, err := url.Parse(value)
if err != nil {
return err
}
i.URL = u
return nil
}

View File

@ -32,10 +32,21 @@
import axios from 'axios'
export default () => {
return axios.create({
const a = axios.create({
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
a.interceptors.response.use((response) => {
return response
}, (error) => {
if (error.response && error.response.status === 401 && error.response.headers && error.response.headers.location) {
window.location = error.response.headers.location
}
return Promise.reject(error)
})
return a
}