Return dedicated error if proxy receives token that is not valid yet.

This can happen for example if the times of the machines running the
signaling server and proxy don't match.
This commit is contained in:
Joachim Bauch 2022-03-23 13:13:32 +01:00
parent fde95ddc93
commit 5101b16df3
No known key found for this signature in database
GPG key ID: 77C1D22D53E15F02
2 changed files with 135 additions and 0 deletions

View file

@ -73,6 +73,7 @@ var (
TimeoutCreatingSubscriber = signaling.NewError("timeout", "Timeout creating subscriber.")
TokenAuthFailed = signaling.NewError("auth_failed", "The token could not be authenticated.")
TokenExpired = signaling.NewError("token_expired", "The token is expired.")
TokenNotValidYet = signaling.NewError("token_not_valid_yet", "The token is not valid yet.")
UnknownClient = signaling.NewError("unknown_client", "Unknown client id given.")
UnsupportedCommand = signaling.NewError("bad_request", "Unsupported command received.")
UnsupportedMessage = signaling.NewError("bad_request", "Unsupported message received.")
@ -865,8 +866,15 @@ func (s *ProxyServer) NewSession(hello *signaling.HelloProxyClientMessage) (*Pro
reason = "unsupported-issuer"
return nil, fmt.Errorf("No key found for issuer")
}
return tokenKey.key, nil
})
if err, ok := err.(*jwt.ValidationError); ok {
if err.Errors&jwt.ValidationErrorIssuedAt == jwt.ValidationErrorIssuedAt {
statsTokenErrorsTotal.WithLabelValues("not-valid-yet").Inc()
return nil, TokenNotValidYet
}
}
if err != nil {
statsTokenErrorsTotal.WithLabelValues(reason).Inc()
return nil, TokenAuthFailed

127
proxy/proxy_server_test.go Normal file
View file

@ -0,0 +1,127 @@
/**
* Standalone signaling server for the Nextcloud Spreed app.
* Copyright (C) 2022 struktur AG
*
* @author Joachim Bauch <bauch@struktur.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"io/ioutil"
"os"
"testing"
"time"
"github.com/dlintw/goconf"
"github.com/golang-jwt/jwt"
"github.com/gorilla/mux"
signaling "github.com/strukturag/nextcloud-spreed-signaling"
)
const (
KeypairSizeForTest = 2048
TokenIdForTest = "foo"
)
func newProxyServerForTest(t *testing.T) (*ProxyServer, *rsa.PrivateKey, func()) {
tempdir, err := ioutil.TempDir("", "test")
if err != nil {
t.Fatalf("could not create temporary folder: %s", err)
}
var server *ProxyServer
shutdown := func() {
if server != nil {
server.Stop()
}
os.RemoveAll(tempdir)
}
r := mux.NewRouter()
key, err := rsa.GenerateKey(rand.Reader, KeypairSizeForTest)
if err != nil {
t.Fatalf("could not generate key: %s", err)
}
priv := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key),
}
privkey, err := ioutil.TempFile(tempdir, "privkey*.pem")
if err != nil {
t.Fatalf("could not create temporary file for private key: %s", err)
}
if err := pem.Encode(privkey, priv); err != nil {
t.Fatalf("could not encode private key: %s", err)
}
pubData, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
if err != nil {
t.Fatalf("could not marshal public key: %s", err)
}
pub := &pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubData,
}
pubkey, err := ioutil.TempFile(tempdir, "pubkey*.pem")
if err != nil {
t.Fatalf("could not create temporary file for public key: %s", err)
}
if err := pem.Encode(pubkey, pub); err != nil {
t.Fatalf("could not encode public key: %s", err)
}
config := goconf.NewConfigFile()
config.AddOption("tokens", TokenIdForTest, pubkey.Name())
if server, err = NewProxyServer(r, "0.0", config); err != nil {
t.Fatalf("could not create server: %s", err)
}
return server, key, shutdown
}
func TestTokenInFuture(t *testing.T) {
server, key, shutdown := newProxyServerForTest(t)
defer shutdown()
claims := &signaling.TokenClaims{
StandardClaims: jwt.StandardClaims{
IssuedAt: time.Now().Add(time.Hour).Unix(),
Issuer: TokenIdForTest,
},
}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
tokenString, err := token.SignedString(key)
if err != nil {
t.Fatalf("could not create token: %s", err)
}
hello := &signaling.HelloProxyClientMessage{
Version: "1.0",
Token: tokenString,
}
session, err := server.NewSession(hello)
if session != nil {
defer session.Close()
t.Errorf("should not have created session")
} else if err != TokenNotValidYet {
t.Errorf("could have failed with TokenNotValidYet, got %s", err)
}
}