@ -4,14 +4,10 @@
package web
package web
import (
import (
"bytes"
"context"
"context"
"crypto/rand"
"crypto/rand"
"encoding/base64"
"encoding/base64"
"encoding/json"
"errors"
"errors"
"fmt"
"io"
"net/http"
"net/http"
"time"
"time"
@ -152,7 +148,7 @@ func (s *Server) getSession(r *http.Request) (*browserSession, *apitype.WhoIsRes
// and stores it back to the session cache. Creating of a new session includes
// and stores it back to the session cache. Creating of a new session includes
// generating a new auth URL from the control server.
// generating a new auth URL from the control server.
func ( s * Server ) newSession ( ctx context . Context , src * apitype . WhoIsResponse ) ( * browserSession , error ) {
func ( s * Server ) newSession ( ctx context . Context , src * apitype . WhoIsResponse ) ( * browserSession , error ) {
d, err := s . getOrAwaitAuth ( ctx , "" , src . Node . ID )
a, err := s . newAuthURL ( ctx , src . Node . ID )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
@ -164,8 +160,8 @@ func (s *Server) newSession(ctx context.Context, src *apitype.WhoIsResponse) (*b
ID : sid ,
ID : sid ,
SrcNode : src . Node . ID ,
SrcNode : src . Node . ID ,
SrcUser : src . UserProfile . ID ,
SrcUser : src . UserProfile . ID ,
AuthID : d . ID ,
AuthID : a . ID ,
AuthURL : d . URL ,
AuthURL : a . URL ,
Created : s . timeNow ( ) ,
Created : s . timeNow ( ) ,
}
}
s . browserSessions . Store ( sid , session )
s . browserSessions . Store ( sid , session )
@ -179,7 +175,7 @@ func (s *Server) awaitUserAuth(ctx context.Context, session *browserSession) err
if session . isAuthorized ( s . timeNow ( ) ) {
if session . isAuthorized ( s . timeNow ( ) ) {
return nil // already authorized
return nil // already authorized
}
}
d , err := s . getOrA waitAuth( ctx , session . AuthID , session . SrcNode )
a , err := s . waitAuthURL ( ctx , session . AuthID , session . SrcNode )
if err != nil {
if err != nil {
// Clean up the session. Doing this on any error from control
// Clean up the session. Doing this on any error from control
// server to avoid the user getting stuck with a bad session
// server to avoid the user getting stuck with a bad session
@ -187,52 +183,13 @@ func (s *Server) awaitUserAuth(ctx context.Context, session *browserSession) err
s . browserSessions . Delete ( session . ID )
s . browserSessions . Delete ( session . ID )
return err
return err
}
}
if d . Complete {
if a . Complete {
session . Authenticated = d . Complete
session . Authenticated = a . Complete
s . browserSessions . Store ( session . ID , session )
s . browserSessions . Store ( session . ID , session )
}
}
return nil
return nil
}
}
// getOrAwaitAuth connects to the control server for user auth,
// with the following behavior:
//
// 1. If authID is provided empty, a new auth URL is created on the control
// server and reported back here, which can then be used to redirect the
// user on the frontend.
// 2. If authID is provided non-empty, the connection to control blocks until
// the user has completed authenticating the associated auth URL,
// or until ctx is canceled.
func ( s * Server ) getOrAwaitAuth ( ctx context . Context , authID string , src tailcfg . NodeID ) ( * tailcfg . WebClientAuthResponse , error ) {
type data struct {
ID string
Src tailcfg . NodeID
}
var b bytes . Buffer
if err := json . NewEncoder ( & b ) . Encode ( data { ID : authID , Src : src } ) ; err != nil {
return nil , err
}
url := "http://" + apitype . LocalAPIHost + "/localapi/v0/debug-web-client"
req , err := http . NewRequestWithContext ( ctx , "POST" , url , & b )
if err != nil {
return nil , err
}
resp , err := s . lc . DoLocalRequest ( req )
if err != nil {
return nil , err
}
body , _ := io . ReadAll ( resp . Body )
resp . Body . Close ( )
if resp . StatusCode != http . StatusOK {
return nil , fmt . Errorf ( "failed request: %s" , body )
}
var authResp * tailcfg . WebClientAuthResponse
if err := json . Unmarshal ( body , & authResp ) ; err != nil {
return nil , err
}
return authResp , nil
}
func ( s * Server ) newSessionID ( ) ( string , error ) {
func ( s * Server ) newSessionID ( ) ( string , error ) {
raw := make ( [ ] byte , 16 )
raw := make ( [ ] byte , 16 )
for i := 0 ; i < 5 ; i ++ {
for i := 0 ; i < 5 ; i ++ {