webmail: rename query string param "token" to "singleUseToken" to be less scary in access logs

these singleusetokens can be redeemed once. so when you see it in the logs, it
can't be used again. they are short-lived anyway.

this change should help prevent me periodically investigating token handling...
This commit is contained in:
Mechiel Lukkien 2024-08-23 15:08:27 +02:00
parent a977082b89
commit 594182aae5
No known key found for this signature in database
10 changed files with 35 additions and 29 deletions

View file

@ -130,9 +130,10 @@ func (w Webmail) Logout(ctx context.Context) {
xcheckf(ctx, err, "logout")
}
// Token returns a token to use for an SSE connection. A token can only be used for
// a single SSE connection. Tokens are stored in memory for a maximum of 1 minute,
// with at most 10 unused tokens (the most recently created) per account.
// Token returns a single-use token to use for an SSE connection. A token can only
// be used for a single SSE connection. Tokens are stored in memory for a maximum
// of 1 minute, with at most 10 unused tokens (the most recently created) per
// account.
func (Webmail) Token(ctx context.Context) string {
reqInfo := ctx.Value(requestInfoCtxKey).(requestInfo)
return sseTokens.xgenerate(ctx, reqInfo.Account.Name, reqInfo.LoginAddress, reqInfo.SessionToken)

View file

@ -55,7 +55,7 @@
},
{
"Name": "Token",
"Docs": "Token returns a token to use for an SSE connection. A token can only be used for\na single SSE connection. Tokens are stored in memory for a maximum of 1 minute,\nwith at most 10 unused tokens (the most recently created) per account.",
"Docs": "Token returns a single-use token to use for an SSE connection. A token can only\nbe used for a single SSE connection. Tokens are stored in memory for a maximum\nof 1 minute, with at most 10 unused tokens (the most recently created) per\naccount.",
"Params": [],
"Returns": [
{

View file

@ -750,9 +750,10 @@ export class Client {
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params) as void
}
// Token returns a token to use for an SSE connection. A token can only be used for
// a single SSE connection. Tokens are stored in memory for a maximum of 1 minute,
// with at most 10 unused tokens (the most recently created) per account.
// Token returns a single-use token to use for an SSE connection. A token can only
// be used for a single SSE connection. Tokens are stored in memory for a maximum
// of 1 minute, with at most 10 unused tokens (the most recently created) per
// account.
async Token(): Promise<string> {
const fn: string = "Token"
const paramTypes: string[][] = []

View file

@ -448,9 +448,10 @@ var api;
const params = [];
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params);
}
// Token returns a token to use for an SSE connection. A token can only be used for
// a single SSE connection. Tokens are stored in memory for a maximum of 1 minute,
// with at most 10 unused tokens (the most recently created) per account.
// Token returns a single-use token to use for an SSE connection. A token can only
// be used for a single SSE connection. Tokens are stored in memory for a maximum
// of 1 minute, with at most 10 unused tokens (the most recently created) per
// account.
async Token() {
const fn = "Token";
const paramTypes = [];

View file

@ -448,9 +448,10 @@ var api;
const params = [];
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params);
}
// Token returns a token to use for an SSE connection. A token can only be used for
// a single SSE connection. Tokens are stored in memory for a maximum of 1 minute,
// with at most 10 unused tokens (the most recently created) per account.
// Token returns a single-use token to use for an SSE connection. A token can only
// be used for a single SSE connection. Tokens are stored in memory for a maximum
// of 1 minute, with at most 10 unused tokens (the most recently created) per
// account.
async Token() {
const fn = "Token";
const paramTypes = [];

View file

@ -489,7 +489,8 @@ type ioErr struct {
}
// serveEvents serves an SSE connection. Authentication is done through a query
// string parameter "token", a one-time-use token returned by the Token API call.
// string parameter "singleUseToken", a one-time-use token returned by the Token
// API call.
func serveEvents(ctx context.Context, log mlog.Log, accountPath string, w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "405 - method not allowed - use get", http.StatusMethodNotAllowed)
@ -504,7 +505,7 @@ func serveEvents(ctx context.Context, log mlog.Log, accountPath string, w http.R
}
q := r.URL.Query()
token := q.Get("token")
token := q.Get("singleUseToken")
if token == "" {
http.Error(w, "400 - bad request - missing credentials", http.StatusBadRequest)
return

View file

@ -131,13 +131,13 @@ func TestView(t *testing.T) {
}
}
testFail("POST", eventsURL+"?token="+tokens[0]+"&request="+string(requestJSON), http.StatusMethodNotAllowed) // Must be GET.
testFail("POST", eventsURL+"?singleUseToken="+tokens[0]+"&request="+string(requestJSON), http.StatusMethodNotAllowed) // Must be GET.
testFail("GET", eventsURL, http.StatusBadRequest) // Missing token.
testFail("GET", eventsURL+"?token="+tokens[0]+"&request="+string(requestJSON), http.StatusBadRequest) // Bad (old) token.
testFail("GET", eventsURL+"?token="+tokens[len(tokens)-5]+"&request=bad", http.StatusBadRequest) // Bad request.
testFail("GET", eventsURL+"?singleUseToken="+tokens[0]+"&request="+string(requestJSON), http.StatusBadRequest) // Bad (old) token.
testFail("GET", eventsURL+"?singleUseToken="+tokens[len(tokens)-5]+"&request=bad", http.StatusBadRequest) // Bad request.
// Start connection for testing and filters below.
req, err := http.NewRequest("GET", eventsURL+"?token="+tokens[len(tokens)-1]+"&request="+string(requestJSON), nil)
req, err := http.NewRequest("GET", eventsURL+"?singleUseToken="+tokens[len(tokens)-1]+"&request="+string(requestJSON), nil)
tcheck(t, err, "making request")
resp, err := http.DefaultClient.Do(req)
tcheck(t, err, "http transaction")
@ -168,7 +168,7 @@ func TestView(t *testing.T) {
}
// Can only use a token once.
testFail("GET", eventsURL+"?token="+tokens[len(tokens)-1]+"&request=bad", http.StatusBadRequest)
testFail("GET", eventsURL+"?singleUseToken="+tokens[len(tokens)-1]+"&request=bad", http.StatusBadRequest)
// Check a few initial query/page combinations.
testConn := func(token, more string, request Request, check func(EventStart, eventReader)) {
@ -176,7 +176,7 @@ func TestView(t *testing.T) {
reqJSON, err := json.Marshal(request)
tcheck(t, err, "marshal request json")
req, err := http.NewRequest("GET", eventsURL+"?token="+token+more+"&request="+string(reqJSON), nil)
req, err := http.NewRequest("GET", eventsURL+"?singleUseToken="+token+more+"&request="+string(reqJSON), nil)
tcheck(t, err, "making request")
resp, err := http.DefaultClient.Do(req)
tcheck(t, err, "http transaction")

View file

@ -194,8 +194,8 @@ func handle(apiHandler http.Handler, isForwarded bool, accountPath string, w htt
log := pkglog.WithContext(ctx).With(slog.String("userauth", ""))
// Server-sent event connection, for all initial data (list of mailboxes), list of
// messages, and all events afterwards. Authenticated through a token in the query
// string, which it got from a Token API call.
// messages, and all events afterwards. Authenticated through a single use token in
// the query string, which it got from a Token API call.
if r.URL.Path == "/events" {
serveEvents(ctx, log, accountPath, w, r)
return

View file

@ -448,9 +448,10 @@ var api;
const params = [];
return await _sherpaCall(this.baseURL, this.authState, { ...this.options }, paramTypes, returnTypes, fn, params);
}
// Token returns a token to use for an SSE connection. A token can only be used for
// a single SSE connection. Tokens are stored in memory for a maximum of 1 minute,
// with at most 10 unused tokens (the most recently created) per account.
// Token returns a single-use token to use for an SSE connection. A token can only
// be used for a single SSE connection. Tokens are stored in memory for a maximum
// of 1 minute, with at most 10 unused tokens (the most recently created) per
// account.
async Token() {
const fn = "Token";
const paramTypes = [];
@ -6911,7 +6912,7 @@ const init = async () => {
}
}
catch (err) { }
eventSource = new window.EventSource('events?token=' + encodeURIComponent(token) + '&request=' + encodeURIComponent(JSON.stringify(request)) + slow);
eventSource = new window.EventSource('events?singleUseToken=' + encodeURIComponent(token) + '&request=' + encodeURIComponent(JSON.stringify(request)) + slow);
let eventID = window.setTimeout(() => dom._kids(statusElem, 'Connecting... '), 1000);
eventSource.addEventListener('open', (e) => {
log('eventsource open', { e });

View file

@ -7208,7 +7208,7 @@ const init = async () => {
}
} catch (err) {}
eventSource = new window.EventSource('events?token=' + encodeURIComponent(token)+'&request='+encodeURIComponent(JSON.stringify(request))+slow)
eventSource = new window.EventSource('events?singleUseToken=' + encodeURIComponent(token)+'&request='+encodeURIComponent(JSON.stringify(request))+slow)
let eventID = window.setTimeout(() => dom._kids(statusElem, 'Connecting... '), 1000)
eventSource.addEventListener('open', (e: Event) => {
log('eventsource open', {e})