1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-12-23 01:20:11 +00:00

Fixed flaw with jellyfin_login; store refresh token in cookies

with jellyfin_login enabled, the username and password vals in the User
struct would be "". If you disabled 'required' on the login form, blank
username and password would allow you in.
This commit is contained in:
Harvey Tindall 2020-08-20 20:20:31 +01:00
parent d144077e62
commit ee3b421566
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
4 changed files with 40 additions and 25 deletions

10
api.go
View File

@ -610,7 +610,15 @@ func (app *appContext) ModifyConfig(gc *gin.Context) {
} }
func (app *appContext) Logout(gc *gin.Context) { func (app *appContext) Logout(gc *gin.Context) {
app.invalidIds = append(app.invalidIds, gc.GetString("userId")) cookie, err := gc.Cookie("refresh")
if err != nil {
app.debug.Printf("Couldn't get cookies: %s", err)
respond(500, "Couldn't fetch cookies", gc)
return
}
app.invalidTokens = append(app.invalidTokens, cookie)
fmt.Println("After appending", cookie, ":", app.invalidTokens)
gc.SetCookie("refresh", "invalid", -1, "/", gc.Request.URL.Hostname(), true, true)
gc.JSON(200, map[string]bool{"success": true}) gc.JSON(200, map[string]bool{"success": true})
} }

26
auth.go
View File

@ -87,18 +87,30 @@ func (app *appContext) GetToken(gc *gin.Context) {
var userId, jfId string var userId, jfId string
for _, user := range app.users { for _, user := range app.users {
if user.Username == creds[0] && user.Password == creds[1] { if user.Username == creds[0] && user.Password == creds[1] {
if creds[0] != "" && creds[1] != "" {
match = true match = true
app.debug.Println("Found existing user")
userId = user.UserID userId = user.UserID
} }
} }
}
if !match { if !match {
if !app.jellyfinLogin { if !app.jellyfinLogin {
app.info.Println("Auth failed: Invalid username and/or password") app.info.Println("Auth failed: Invalid username and/or password")
respond(401, "Unauthorized", gc) respond(401, "Unauthorized", gc)
return return
} }
if creds[1] == "" { cookie, err := gc.Cookie("refresh")
token, err := jwt.Parse(creds[0], func(token *jwt.Token) (interface{}, error) { if err == nil && cookie != "" && creds[0] == "" && creds[1] == "" {
fmt.Println("Checking:", cookie)
for _, token := range app.invalidTokens {
if cookie == token {
app.debug.Printf("Auth denied: Refresh token in blocklist")
respond(401, "Unauthorized", gc)
return
}
}
token, err := jwt.Parse(cookie, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
app.debug.Printf("Invalid JWT signing method %s", token.Header["alg"]) app.debug.Printf("Invalid JWT signing method %s", token.Header["alg"])
return nil, fmt.Errorf("Unexpected signing method %v", token.Header["alg"]) return nil, fmt.Errorf("Unexpected signing method %v", token.Header["alg"])
@ -111,13 +123,6 @@ func (app *appContext) GetToken(gc *gin.Context) {
return return
} }
claims, ok := token.Claims.(jwt.MapClaims) claims, ok := token.Claims.(jwt.MapClaims)
for _, id := range app.invalidIds {
if claims["id"].(string) == id {
app.debug.Printf("Auth denied: Refresh token in blocklist")
respond(401, "Unauthorized", gc)
return
}
}
expiryUnix, err := strconv.ParseInt(claims["exp"].(string), 10, 64) expiryUnix, err := strconv.ParseInt(claims["exp"].(string), 10, 64)
if err != nil { if err != nil {
app.debug.Printf("Auth denied: %s", err) app.debug.Printf("Auth denied: %s", err)
@ -168,7 +173,8 @@ func (app *appContext) GetToken(gc *gin.Context) {
if err != nil { if err != nil {
respond(500, "Error generating token", gc) respond(500, "Error generating token", gc)
} }
resp := map[string]string{"token": token, "refresh": refresh} resp := map[string]string{"token": token}
gc.SetCookie("refresh", refresh, (3600 * 24), "/", gc.Request.URL.Hostname(), true, true)
gc.JSON(200, resp) gc.JSON(200, resp)
} }

View File

@ -531,7 +531,7 @@ document.getElementById('inviteForm').onsubmit = function() {
return false; return false;
}; };
function tryLogin(username, password, modal, button) { function tryLogin(username, password, modal, button, callback) {
let req = new XMLHttpRequest(); let req = new XMLHttpRequest();
req.responseType = 'json'; req.responseType = 'json';
req.onreadystatechange = function() { req.onreadystatechange = function() {
@ -561,7 +561,6 @@ function tryLogin(username, password, modal, button) {
} else { } else {
const data = this.response; const data = this.response;
window.token = data['token']; window.token = data['token'];
document.cookie = "refresh=" + data['refresh'];
generateInvites(); generateInvites();
const interval = setInterval(function() { generateInvites(); }, 60 * 1000); const interval = setInterval(function() { generateInvites(); }, 60 * 1000);
let day = document.getElementById('days'); let day = document.getElementById('days');
@ -579,6 +578,9 @@ function tryLogin(username, password, modal, button) {
} }
document.getElementById('logoutButton').setAttribute('style', ''); document.getElementById('logoutButton').setAttribute('style', '');
} }
if (typeof callback === "function") {
callback(this.status);
}
} }
}; };
req.open("GET", "/getToken", true); req.open("GET", "/getToken", true);
@ -600,7 +602,7 @@ document.getElementById('loginForm').onsubmit = function() {
button.innerHTML = button.innerHTML =
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' + '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' +
'Loading...'; 'Loading...';
tryLogin(details['username'], details['password'], true, button) tryLogin(username = details['username'], password = details['password'], modal = true, button = button)
return false; return false;
}; };
@ -807,12 +809,12 @@ document.getElementById('openUsers').onclick = function () {
generateInvites(empty = true); generateInvites(empty = true);
let refreshToken = getCookie("refresh") tryLogin("", "", false, callback = function(code){
if (refreshToken != "") { console.log(code);
tryLogin(refreshToken, "", false) if (code != 200) {
} else {
loginModal.show(); loginModal.show();
} }
});
document.getElementById('logoutButton').onclick = function () { document.getElementById('logoutButton').onclick = function () {
let req = new XMLHttpRequest(); let req = new XMLHttpRequest();
@ -822,7 +824,6 @@ document.getElementById('logoutButton').onclick = function () {
req.onreadystatechange = function() { req.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) { if (this.readyState == 4 && this.status == 200) {
window.token = ''; window.token = '';
document.cookie = 'refresh=;';
location.reload(); location.reload();
return false; return false;
} }

View File

@ -42,7 +42,7 @@ type appContext struct {
bsVersion int bsVersion int
jellyfinLogin bool jellyfinLogin bool
users []User users []User
invalidIds []string invalidTokens []string
jf Jellyfin jf Jellyfin
authJf Jellyfin authJf Jellyfin
datePattern string datePattern string
@ -329,7 +329,7 @@ func main() {
router.Use(static.Serve("/invite/", static.LocalFile(filepath.Join(app.local_path, "static"), false))) router.Use(static.Serve("/invite/", static.LocalFile(filepath.Join(app.local_path, "static"), false)))
router.GET("/invite/:invCode", app.InviteProxy) router.GET("/invite/:invCode", app.InviteProxy)
api := router.Group("/", app.webAuth()) api := router.Group("/", app.webAuth())
api.POST("/logout", app.Logout) router.POST("/logout", app.Logout)
api.POST("/generateInvite", app.GenerateInvite) api.POST("/generateInvite", app.GenerateInvite)
api.GET("/getInvites", app.GetInvites) api.GET("/getInvites", app.GetInvites)
api.POST("/setNotify", app.SetNotify) api.POST("/setNotify", app.SetNotify)