sso: expire tokens after usage

This commit is contained in:
Adhityaa Chandrasekar 2019-04-20 23:25:35 -04:00
parent fa2ccfe42e
commit 6317b384d9
6 changed files with 82 additions and 13 deletions

View File

@ -4,7 +4,7 @@ import (
"net/http"
)
func domainSsoNew(domain string) (string, error) {
func domainSsoSecretNew(domain string) (string, error) {
if domain == "" {
return "", errorMissingField
}
@ -29,7 +29,7 @@ func domainSsoNew(domain string) (string, error) {
return ssoSecret, nil
}
func domainSsoNewHandler(w http.ResponseWriter, r *http.Request) {
func domainSsoSecretNewHandler(w http.ResponseWriter, r *http.Request) {
type request struct {
OwnerToken *string `json:"ownerToken"`
Domain *string `json:"domain"`
@ -59,7 +59,7 @@ func domainSsoNewHandler(w http.ResponseWriter, r *http.Request) {
return
}
ssoSecret, err := domainSsoNew(domain)
ssoSecret, err := domainSsoSecretNew(domain)
if err != nil {
bodyMarshal(w, response{"success": false, "message": err.Error()})
return

View File

@ -1,5 +1,9 @@
package main
import (
"time"
)
type ssoPayload struct {
Domain string `json:"domain"`
Token string `json:"token"`
@ -8,3 +12,50 @@ type ssoPayload struct {
Link string `json:"link"`
Photo string `json:"photo"`
}
func ssoTokenNew(domain string, commenterToken string) (string, error) {
token, err := randomHex(32)
if err != nil {
logger.Errorf("error generating SSO token hex: %v", err)
return "", errorInternal
}
statement := `
INSERT INTO
ssoTokens (token, domain, commenterToken, creationDate)
VALUES ($1, $2, $3, $4 );
`
_, err = db.Exec(statement, token, domain, commenterToken, time.Now().UTC())
if err != nil {
logger.Errorf("error inserting SSO token: %v", err)
return "", errorInternal
}
return token, nil
}
func ssoTokenExtract(token string) (string, string, error) {
statement := `
SELECT domain, commenterToken
FROM ssoTokens
WHERE token = $1;
`
row := db.QueryRow(statement, token)
var domain string
var commenterToken string
if err := row.Scan(&domain, &commenterToken); err != nil {
return "", "", errorNoSuchToken
}
statement = `
DELETE FROM ssoTokens
WHERE token = $1;
`
if _, err := db.Exec(statement, token); err != nil {
logger.Errorf("cannot delete SSO token after usage: %v", err)
return "", "", errorInternal
}
return domain, commenterToken, nil
}

View File

@ -32,7 +32,7 @@ func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
return
}
if payload.Domain == "" || payload.Token == "" || payload.Email == "" || payload.Name == "" {
if payload.Token == "" || payload.Email == "" || payload.Name == "" {
fmt.Fprintf(w, "Error: %s\n", errorMissingField.Error())
return
}
@ -45,7 +45,13 @@ func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
payload.Photo = "undefined"
}
d, err := domainGet(payload.Domain)
domain, commenterToken, err := ssoTokenExtract(payload.Token)
if err != nil {
fmt.Fprintf(w, "Error: %s\n", err.Error())
return
}
d, err := domainGet(domain)
if err != nil {
if err == errorNoSuchDomain {
fmt.Fprintf(w, "Error: %s\n", err.Error())
@ -76,13 +82,13 @@ func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
return
}
_, err = commenterGetByCommenterToken(payload.Token)
_, err = commenterGetByCommenterToken(commenterToken)
if err != nil && err != errorNoSuchToken {
fmt.Fprintf(w, "Error: %s\n", err.Error())
return
}
c, err := commenterGetByEmail("sso:"+d.Domain, payload.Email)
c, err := commenterGetByEmail("sso:"+domain, payload.Email)
if err != nil && err != errorNoSuchCommenter {
fmt.Fprintf(w, "Error: %s\n", err.Error())
return
@ -92,7 +98,7 @@ func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
// TODO: in case of returning users, update the information we have on record?
if err == errorNoSuchCommenter {
commenterHex, err = commenterNew(payload.Email, payload.Name, payload.Link, payload.Photo, "sso:"+d.Domain, "")
commenterHex, err = commenterNew(payload.Email, payload.Name, payload.Link, payload.Photo, "sso:"+domain, "")
if err != nil {
fmt.Fprintf(w, "Error: %s", err.Error())
return
@ -101,7 +107,7 @@ func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
commenterHex = c.CommenterHex
}
if err = commenterSessionUpdate(payload.Token, commenterHex); err != nil {
if err = commenterSessionUpdate(commenterToken, commenterHex); err != nil {
fmt.Fprintf(w, "Error: %s\n", err.Error())
return
}

View File

@ -53,9 +53,15 @@ func ssoRedirectHandler(w http.ResponseWriter, r *http.Request) {
return
}
tokenBytes, err := hex.DecodeString(commenterToken)
token, err := ssoTokenNew(domain, commenterToken)
if err != nil {
logger.Errorf("cannot decode hex commenterToken: %v", err)
fmt.Fprintf(w, "Error: %s\n", err.Error())
return
}
tokenBytes, err := hex.DecodeString(token)
if err != nil {
logger.Errorf("cannot decode hex token: %v", err)
fmt.Fprintf(w, "Error: %s\n", errorInternal.Error())
return
}
@ -74,7 +80,7 @@ func ssoRedirectHandler(w http.ResponseWriter, r *http.Request) {
}
q := u.Query()
q.Set("token", commenterToken)
q.Set("token", token)
q.Set("hmac", signature)
u.RawQuery = q.Encode()

View File

@ -15,7 +15,7 @@ func apiRouterInit(router *mux.Router) error {
router.HandleFunc("/api/domain/new", domainNewHandler).Methods("POST")
router.HandleFunc("/api/domain/delete", domainDeleteHandler).Methods("POST")
router.HandleFunc("/api/domain/clear", domainClearHandler).Methods("POST")
router.HandleFunc("/api/domain/sso/new", domainSsoNewHandler).Methods("POST")
router.HandleFunc("/api/domain/sso/new", domainSsoSecretNewHandler).Methods("POST")
router.HandleFunc("/api/domain/list", domainListHandler).Methods("POST")
router.HandleFunc("/api/domain/update", domainUpdateHandler).Methods("POST")
router.HandleFunc("/api/domain/moderator/new", domainModeratorNewHandler).Methods("POST")

View File

@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS ssoTokens (
token TEXT NOT NULL UNIQUE PRIMARY KEY ,
domain TEXT NOT NULL ,
commenterToken TEXT NOT NULL ,
creationDate TIMESTAMP NOT NULL
);