mirror of
https://github.com/twofas/2fas-server.git
synced 2024-12-04 16:20:13 +01:00
chore: rework endpoints for push
This commit is contained in:
parent
a8317c1af6
commit
0eb932017b
@ -101,17 +101,6 @@ func confirmSyncMobileRequest(connectionToken string) (string, error) {
|
||||
return resp.ProxyToken, nil
|
||||
}
|
||||
|
||||
func getMobileToken(fcm string) (string, error) {
|
||||
var resp struct {
|
||||
MobileSyncConfirmToken string `json:"mobile_sync_confirm_token"`
|
||||
}
|
||||
if err := request("GET", fmt.Sprintf("/mobile/sync/%s/token", fcm), "", nil, &resp); err != nil {
|
||||
return "", fmt.Errorf("failed to get mobile token: %w", err)
|
||||
}
|
||||
|
||||
return resp.MobileSyncConfirmToken, nil
|
||||
}
|
||||
|
||||
func request(method, path, auth string, req, resp interface{}) error {
|
||||
url := getApiURL() + path
|
||||
var body io.Reader
|
||||
@ -151,3 +140,32 @@ func request(method, path, auth string, req, resp interface{}) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type RequestSyncResponse struct {
|
||||
BrowserExtensionWaitToken string `json:"browser_extension_wait_token"`
|
||||
MobileConfirmToken string `json:"mobile_confirm_token"`
|
||||
}
|
||||
|
||||
func browserExtensionRequestSync(token string) (RequestSyncResponse, error) {
|
||||
var resp RequestSyncResponse
|
||||
|
||||
if err := request("POST", "/browser_extension/sync/request", token, nil, &resp); err != nil {
|
||||
return resp, fmt.Errorf("failed to configure browser: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func browserExtensionPushToMobile(token string) error {
|
||||
req := struct {
|
||||
Body string `json:"push_body"`
|
||||
}{
|
||||
Body: "sent from browser extension",
|
||||
}
|
||||
resp := struct{}{}
|
||||
if err := request("POST", "/browser_extension/sync/push", token, req, &resp); err != nil {
|
||||
return fmt.Errorf("failed to configure browser: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ func TestSyncHappyFlow(t *testing.T) {
|
||||
|
||||
browserExtensionDone := make(chan struct{})
|
||||
mobileParingDone := make(chan struct{})
|
||||
confirmMobileChannel := make(chan string)
|
||||
|
||||
fcm := uuid.NewString()
|
||||
deviceID := getDeviceID()
|
||||
@ -26,7 +27,15 @@ func TestSyncHappyFlow(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
proxyToken, err := browserExtensionWaitForSyncConfirm(syncToken)
|
||||
requestSyncResp, err := browserExtensionRequestSync(syncToken)
|
||||
if err != nil {
|
||||
t.Errorf("Error when Browser Extension requested sync confirm: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
confirmMobileChannel <- requestSyncResp.MobileConfirmToken
|
||||
|
||||
proxyToken, err := browserExtensionWaitForSyncConfirm(requestSyncResp.BrowserExtensionWaitToken)
|
||||
if err != nil {
|
||||
t.Errorf("Error when Browser Extension waited for sync confirm: %v", err)
|
||||
return
|
||||
@ -51,11 +60,7 @@ func TestSyncHappyFlow(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
confirmToken, err := getMobileToken(fcm)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to fetch mobile token: %v", err)
|
||||
return
|
||||
}
|
||||
confirmToken := <-confirmMobileChannel
|
||||
|
||||
proxyToken, err := confirmSyncMobile(confirmToken)
|
||||
if err != nil {
|
||||
|
@ -24,7 +24,7 @@ func getWsURL() string {
|
||||
}
|
||||
|
||||
func browserExtensionWaitForSyncConfirm(token string) (string, error) {
|
||||
url := getWsURL() + "/browser_extension/sync/request"
|
||||
url := getWsURL() + "/browser_extension/sync/wait"
|
||||
|
||||
var resp struct {
|
||||
BrowserExtensionSyncToken string `json:"browser_extension_proxy_token"`
|
||||
|
@ -14,7 +14,6 @@ import (
|
||||
|
||||
"github.com/twofas/2fas-server/config"
|
||||
httphelpers "github.com/twofas/2fas-server/internal/common/http"
|
||||
"github.com/twofas/2fas-server/internal/common/logging"
|
||||
"github.com/twofas/2fas-server/internal/common/recovery"
|
||||
"github.com/twofas/2fas-server/internal/pass/connection"
|
||||
"github.com/twofas/2fas-server/internal/pass/pairing"
|
||||
@ -100,16 +99,14 @@ func NewServer(cfg config.PassConfig) *Server {
|
||||
router.POST("/mobile/pairing/confirm", pairing.MobileConfirmHandler(pairingApp))
|
||||
router.GET("/mobile/pairing/proxy", pairing.MobileProxyWSHandler(pairingApp, proxyPairingApp))
|
||||
|
||||
router.GET("/browser_extension/sync/request", sync.ExtensionRequestSync(syncApp))
|
||||
router.POST("/browser_extension/sync/request", sync.ExtensionRequestSync(syncApp))
|
||||
router.POST("/browser_extension/sync/push", sync.ExtensionRequestPush(syncApp))
|
||||
router.GET("/browser_extension/sync/wait", sync.ExtensionRequestWait(syncApp))
|
||||
|
||||
router.GET("/browser_extension/sync/proxy", sync.ExtensionProxyWSHandler(syncApp, proxySyncApp))
|
||||
router.POST("/mobile/sync/confirm", sync.MobileConfirmHandler(syncApp))
|
||||
router.GET("/mobile/sync/proxy", sync.MobileProxyWSHandler(syncApp, proxySyncApp))
|
||||
|
||||
if cfg.FakeMobilePush {
|
||||
logging.Info("Enabled '/mobile/sync/:fcm/token' endpoint. This should happen in test env only!")
|
||||
router.GET("/mobile/sync/:fcm/token", sync.MobileGenerateSyncToken(syncApp))
|
||||
}
|
||||
|
||||
return &Server{
|
||||
router: router,
|
||||
addr: cfg.Addr,
|
||||
|
@ -59,6 +59,7 @@ const (
|
||||
ConnectionTypeBrowserExtensionWait ConnectionType = "be/wait"
|
||||
ConnectionTypeBrowserExtensionProxy ConnectionType = "be/proxy"
|
||||
ConnectionTypeBrowserExtensionSyncRequest ConnectionType = "be/sync/request"
|
||||
ConnectionTypeBrowserExtensionSyncWait ConnectionType = "be/sync/wait"
|
||||
ConnectionTypeBrowserExtensionSync ConnectionType = "be/sync/proxy"
|
||||
ConnectionTypeMobileProxy ConnectionType = "mobile/proxy"
|
||||
ConnectionTypeMobileConfirm ConnectionType = "mobile/confirm"
|
||||
|
@ -16,6 +16,15 @@ func (s *Syncing) VerifyExtRequestSyncToken(ctx context.Context, proxyToken stri
|
||||
return fcmToken, nil
|
||||
}
|
||||
|
||||
// VerifyExtWaitForSyncToken verifies wait for sync request token and returns fcm_token.
|
||||
func (s *Syncing) VerifyExtWaitForSyncToken(ctx context.Context, proxyToken string) (string, error) {
|
||||
fcmToken, err := s.signSvc.CanI(proxyToken, sign.ConnectionTypeBrowserExtensionSyncWait)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to check token signature: %w", err)
|
||||
}
|
||||
return fcmToken, nil
|
||||
}
|
||||
|
||||
// VerifyExtSyncToken verifies sync token and returns fcm_token.
|
||||
func (s *Syncing) VerifyExtSyncToken(ctx context.Context, proxyToken string) (string, error) {
|
||||
fcmToken, err := s.signSvc.CanI(proxyToken, sign.ConnectionTypeBrowserExtensionSync)
|
||||
|
@ -13,7 +13,7 @@ import (
|
||||
|
||||
func ExtensionRequestSync(syncingApp *Syncing) gin.HandlerFunc {
|
||||
return func(gCtx *gin.Context) {
|
||||
token, err := connection.TokenFromWSProtocol(gCtx.Request)
|
||||
token, err := tokenFromRequest(gCtx)
|
||||
if err != nil {
|
||||
logging.Errorf("Failed to get token from request: %v", err)
|
||||
gCtx.Status(http.StatusForbidden)
|
||||
@ -26,8 +26,67 @@ func ExtensionRequestSync(syncingApp *Syncing) gin.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := syncingApp.RequestSync(gCtx, fcmToken)
|
||||
if err != nil {
|
||||
logging.Errorf("Failed to request sync: %v", err)
|
||||
gCtx.Status(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
gCtx.JSON(http.StatusOK, resp)
|
||||
}
|
||||
}
|
||||
|
||||
type PushToMobileRequest struct {
|
||||
Body string `json:"push_body"`
|
||||
}
|
||||
|
||||
func ExtensionRequestPush(syncingApp *Syncing) gin.HandlerFunc {
|
||||
return func(gCtx *gin.Context) {
|
||||
log := logging.FromContext(gCtx.Request.Context())
|
||||
|
||||
token, err := tokenFromRequest(gCtx)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get token from request: %v", err)
|
||||
gCtx.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
fcmToken, err := syncingApp.VerifyExtWaitForSyncToken(gCtx, token)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to verify proxy token: %v", err)
|
||||
gCtx.String(http.StatusUnauthorized, "Invalid auth token")
|
||||
return
|
||||
}
|
||||
var req PushToMobileRequest
|
||||
if err := gCtx.BindJSON(&req); err != nil {
|
||||
gCtx.String(http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log.Infof("Send push to mobile %q: %q", fcmToken, req.Body)
|
||||
|
||||
gCtx.Status(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func ExtensionRequestWait(syncingApp *Syncing) gin.HandlerFunc {
|
||||
return func(gCtx *gin.Context) {
|
||||
log := logging.FromContext(gCtx.Request.Context())
|
||||
|
||||
token, err := connection.TokenFromWSProtocol(gCtx.Request)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to get token from request: %v", err)
|
||||
gCtx.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
fcmToken, err := syncingApp.VerifyExtWaitForSyncToken(gCtx, token)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to verify proxy token: %v", err)
|
||||
gCtx.String(http.StatusUnauthorized, "Invalid auth token")
|
||||
return
|
||||
}
|
||||
|
||||
if err := syncingApp.ServeSyncingRequestWS(gCtx.Writer, gCtx.Request, fcmToken); err != nil {
|
||||
logging.Errorf("Failed to verify proxy token: %v", err)
|
||||
log.Errorf("Failed to verify proxy token: %v", err)
|
||||
gCtx.Status(http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
@ -114,20 +173,6 @@ func MobileProxyWSHandler(syncingApp *Syncing, proxy *connection.ProxyServer) gi
|
||||
}
|
||||
}
|
||||
|
||||
func MobileGenerateSyncToken(syncingApp *Syncing) gin.HandlerFunc {
|
||||
return func(gCtx *gin.Context) {
|
||||
fcm := gCtx.Param("fcm")
|
||||
if fcm == "" {
|
||||
gCtx.Status(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if err := syncingApp.sendMobileToken(fcm, gCtx.Writer); err != nil {
|
||||
logging.Errorf("Failed to send mobile token: %v", err)
|
||||
gCtx.Status(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tokenFromRequest(gCtx *gin.Context) (string, error) {
|
||||
tokenHeader := gCtx.GetHeader("Authorization")
|
||||
if tokenHeader == "" {
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
"github.com/twofas/2fas-server/internal/common/logging"
|
||||
@ -37,10 +38,6 @@ const (
|
||||
syncTokenValidityDuration = 3 * time.Minute
|
||||
)
|
||||
|
||||
type ConfigureBrowserExtensionRequest struct {
|
||||
ExtensionID string `json:"extension_id"`
|
||||
}
|
||||
|
||||
type ConfigureBrowserExtensionResponse struct {
|
||||
BrowserExtensionPairingToken string `json:"browser_extension_pairing_token"`
|
||||
ConnectionToken string `json:"connection_token"`
|
||||
@ -51,11 +48,6 @@ type ExtensionWaitForConnectionInput struct {
|
||||
HttpReq *http.Request
|
||||
}
|
||||
|
||||
type RequestSyncResponse struct {
|
||||
BrowserExtensionProxyToken string `json:"browser_extension_proxy_token"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type MobileSyncPayload struct {
|
||||
MobileSyncToken string `json:"mobile_sync_token"`
|
||||
}
|
||||
@ -112,6 +104,11 @@ func (s *Syncing) requestSync(ctx context.Context, fcmToken string) {
|
||||
s.store.RequestSync(fcmToken)
|
||||
}
|
||||
|
||||
type WaitForSyncResponse struct {
|
||||
BrowserExtensionProxyToken string `json:"browser_extension_proxy_token"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
func (s *Syncing) sendTokenAndCloseConn(fcmToken string, conn *websocket.Conn) error {
|
||||
extProxyToken, err := s.signSvc.SignAndEncode(sign.Message{
|
||||
ConnectionID: fcmToken,
|
||||
@ -122,7 +119,7 @@ func (s *Syncing) sendTokenAndCloseConn(fcmToken string, conn *websocket.Conn) e
|
||||
return fmt.Errorf("failed to generate ext proxy token: %v", err)
|
||||
}
|
||||
|
||||
if err := conn.WriteJSON(RequestSyncResponse{
|
||||
if err := conn.WriteJSON(WaitForSyncResponse{
|
||||
BrowserExtensionProxyToken: extProxyToken,
|
||||
Status: "ok",
|
||||
}); err != nil {
|
||||
@ -178,3 +175,31 @@ func (s *Syncing) confirmSync(ctx context.Context, fcmToken string) (ConfirmSync
|
||||
|
||||
return ConfirmSyncResponse{ProxyToken: mobileProxyToken}, nil
|
||||
}
|
||||
|
||||
type RequestSyncResponse struct {
|
||||
BrowserExtensionWaitToken string `json:"browser_extension_wait_token"`
|
||||
MobileConfirmToken string `json:"mobile_confirm_token"`
|
||||
}
|
||||
|
||||
func (s *Syncing) RequestSync(ctx *gin.Context, token string) (RequestSyncResponse, error) {
|
||||
mobileConfirmToken, err := s.signSvc.SignAndEncode(sign.Message{
|
||||
ConnectionID: token,
|
||||
ExpiresAt: time.Now().Add(syncTokenValidityDuration),
|
||||
ConnectionType: sign.ConnectionTypeMobileSyncConfirm,
|
||||
})
|
||||
if err != nil {
|
||||
return RequestSyncResponse{}, fmt.Errorf("failed to generate mobile confirm token: %v", err)
|
||||
}
|
||||
browserExtensionWaitToken, err := s.signSvc.SignAndEncode(sign.Message{
|
||||
ConnectionID: token,
|
||||
ExpiresAt: time.Now().Add(syncTokenValidityDuration),
|
||||
ConnectionType: sign.ConnectionTypeBrowserExtensionSyncWait,
|
||||
})
|
||||
if err != nil {
|
||||
return RequestSyncResponse{}, fmt.Errorf("failed to generate browser extension wait token: %v", err)
|
||||
}
|
||||
return RequestSyncResponse{
|
||||
BrowserExtensionWaitToken: browserExtensionWaitToken,
|
||||
MobileConfirmToken: mobileConfirmToken,
|
||||
}, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user