sso: expire tokens after usage
This commit is contained in:
parent
fa2ccfe42e
commit
6317b384d9
@ -4,7 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func domainSsoNew(domain string) (string, error) {
|
func domainSsoSecretNew(domain string) (string, error) {
|
||||||
if domain == "" {
|
if domain == "" {
|
||||||
return "", errorMissingField
|
return "", errorMissingField
|
||||||
}
|
}
|
||||||
@ -29,7 +29,7 @@ func domainSsoNew(domain string) (string, error) {
|
|||||||
return ssoSecret, nil
|
return ssoSecret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func domainSsoNewHandler(w http.ResponseWriter, r *http.Request) {
|
func domainSsoSecretNewHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
type request struct {
|
type request struct {
|
||||||
OwnerToken *string `json:"ownerToken"`
|
OwnerToken *string `json:"ownerToken"`
|
||||||
Domain *string `json:"domain"`
|
Domain *string `json:"domain"`
|
||||||
@ -59,7 +59,7 @@ func domainSsoNewHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ssoSecret, err := domainSsoNew(domain)
|
ssoSecret, err := domainSsoSecretNew(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
bodyMarshal(w, response{"success": false, "message": err.Error()})
|
||||||
return
|
return
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type ssoPayload struct {
|
type ssoPayload struct {
|
||||||
Domain string `json:"domain"`
|
Domain string `json:"domain"`
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
@ -8,3 +12,50 @@ type ssoPayload struct {
|
|||||||
Link string `json:"link"`
|
Link string `json:"link"`
|
||||||
Photo string `json:"photo"`
|
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
|
||||||
|
}
|
||||||
|
@ -32,7 +32,7 @@ func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
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())
|
fmt.Fprintf(w, "Error: %s\n", errorMissingField.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -45,7 +45,13 @@ func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
payload.Photo = "undefined"
|
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 != nil {
|
||||||
if err == errorNoSuchDomain {
|
if err == errorNoSuchDomain {
|
||||||
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
@ -76,13 +82,13 @@ func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = commenterGetByCommenterToken(payload.Token)
|
_, err = commenterGetByCommenterToken(commenterToken)
|
||||||
if err != nil && err != errorNoSuchToken {
|
if err != nil && err != errorNoSuchToken {
|
||||||
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := commenterGetByEmail("sso:"+d.Domain, payload.Email)
|
c, err := commenterGetByEmail("sso:"+domain, payload.Email)
|
||||||
if err != nil && err != errorNoSuchCommenter {
|
if err != nil && err != errorNoSuchCommenter {
|
||||||
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
return
|
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?
|
// TODO: in case of returning users, update the information we have on record?
|
||||||
if err == errorNoSuchCommenter {
|
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 {
|
if err != nil {
|
||||||
fmt.Fprintf(w, "Error: %s", err.Error())
|
fmt.Fprintf(w, "Error: %s", err.Error())
|
||||||
return
|
return
|
||||||
@ -101,7 +107,7 @@ func ssoCallbackHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
commenterHex = c.CommenterHex
|
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())
|
fmt.Fprintf(w, "Error: %s\n", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -53,9 +53,15 @@ func ssoRedirectHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenBytes, err := hex.DecodeString(commenterToken)
|
token, err := ssoTokenNew(domain, commenterToken)
|
||||||
if err != nil {
|
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())
|
fmt.Fprintf(w, "Error: %s\n", errorInternal.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -74,7 +80,7 @@ func ssoRedirectHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
q.Set("token", commenterToken)
|
q.Set("token", token)
|
||||||
q.Set("hmac", signature)
|
q.Set("hmac", signature)
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ func apiRouterInit(router *mux.Router) error {
|
|||||||
router.HandleFunc("/api/domain/new", domainNewHandler).Methods("POST")
|
router.HandleFunc("/api/domain/new", domainNewHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/domain/delete", domainDeleteHandler).Methods("POST")
|
router.HandleFunc("/api/domain/delete", domainDeleteHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/domain/clear", domainClearHandler).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/list", domainListHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/domain/update", domainUpdateHandler).Methods("POST")
|
router.HandleFunc("/api/domain/update", domainUpdateHandler).Methods("POST")
|
||||||
router.HandleFunc("/api/domain/moderator/new", domainModeratorNewHandler).Methods("POST")
|
router.HandleFunc("/api/domain/moderator/new", domainModeratorNewHandler).Methods("POST")
|
||||||
|
6
db/20190420231030-sso-tokens.sql
Normal file
6
db/20190420231030-sso-tokens.sql
Normal 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
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user