From 6317b384d968ec8f311ad7c4b596072b75bc757f Mon Sep 17 00:00:00 2001 From: Adhityaa Chandrasekar Date: Sat, 20 Apr 2019 23:25:35 -0400 Subject: [PATCH] sso: expire tokens after usage --- api/domain_sso.go | 6 ++-- api/oauth_sso.go | 51 ++++++++++++++++++++++++++++++++ api/oauth_sso_callback.go | 18 +++++++---- api/oauth_sso_redirect.go | 12 ++++++-- api/router_api.go | 2 +- db/20190420231030-sso-tokens.sql | 6 ++++ 6 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 db/20190420231030-sso-tokens.sql diff --git a/api/domain_sso.go b/api/domain_sso.go index 0b74976..64d0c9e 100644 --- a/api/domain_sso.go +++ b/api/domain_sso.go @@ -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 diff --git a/api/oauth_sso.go b/api/oauth_sso.go index 0b370dc..177ef0f 100644 --- a/api/oauth_sso.go +++ b/api/oauth_sso.go @@ -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 +} diff --git a/api/oauth_sso_callback.go b/api/oauth_sso_callback.go index 914f59a..86fb18f 100644 --- a/api/oauth_sso_callback.go +++ b/api/oauth_sso_callback.go @@ -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 } diff --git a/api/oauth_sso_redirect.go b/api/oauth_sso_redirect.go index 92f5112..994fb22 100644 --- a/api/oauth_sso_redirect.go +++ b/api/oauth_sso_redirect.go @@ -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() diff --git a/api/router_api.go b/api/router_api.go index ad196e1..61fd0cc 100644 --- a/api/router_api.go +++ b/api/router_api.go @@ -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") diff --git a/db/20190420231030-sso-tokens.sql b/db/20190420231030-sso-tokens.sql new file mode 100644 index 0000000..329a4fb --- /dev/null +++ b/db/20190420231030-sso-tokens.sql @@ -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 +);