mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-16 03:31:39 +00:00
ipn: add Login backend command for sign-in with token
The StartLoginInteractive command is for delegating the sign-in flow to a browser. The Android Gooogle Sign-In SDK inverts the flow by giving the client ID tokens. Add a new backend command for accepting such tokens by exposing the existing controlclient.Client.Login support for OAuth2 tokens. Introduce a custom TokenType to distinguish ID tokens from other OAuth2 tokens. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
parent
969206fe88
commit
6e8f0860af
@ -8,6 +8,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
@ -27,6 +28,10 @@ const (
|
|||||||
Running
|
Running
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GoogleIDToken Type is the oauth2.Token.TokenType for the Google
|
||||||
|
// ID tokens used by the Android client.
|
||||||
|
const GoogleIDTokenType = "ts_android_google_login"
|
||||||
|
|
||||||
func (s State) String() string {
|
func (s State) String() string {
|
||||||
return [...]string{"NoState", "NeedsLogin", "NeedsMachineAuth",
|
return [...]string{"NoState", "NeedsLogin", "NeedsMachineAuth",
|
||||||
"Stopped", "Starting", "Running"}[s]
|
"Stopped", "Starting", "Running"}[s]
|
||||||
@ -129,6 +134,8 @@ type Backend interface {
|
|||||||
// flow. This should trigger a new BrowseToURL notification
|
// flow. This should trigger a new BrowseToURL notification
|
||||||
// eventually.
|
// eventually.
|
||||||
StartLoginInteractive()
|
StartLoginInteractive()
|
||||||
|
// Login logs in with an OAuth2 token.
|
||||||
|
Login(token *oauth2.Token)
|
||||||
// Logout terminates the current login session and stops the
|
// Logout terminates the current login session and stops the
|
||||||
// wireguard engine.
|
// wireguard engine.
|
||||||
Logout()
|
Logout()
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
)
|
)
|
||||||
@ -42,6 +43,14 @@ func (b *FakeBackend) newState(s State) {
|
|||||||
func (b *FakeBackend) StartLoginInteractive() {
|
func (b *FakeBackend) StartLoginInteractive() {
|
||||||
u := b.serverURL + "/this/is/fake"
|
u := b.serverURL + "/this/is/fake"
|
||||||
b.notify(Notify{BrowseToURL: &u})
|
b.notify(Notify{BrowseToURL: &u})
|
||||||
|
b.login()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *FakeBackend) Login(token *oauth2.Token) {
|
||||||
|
b.login()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *FakeBackend) login() {
|
||||||
b.newState(NeedsMachineAuth)
|
b.newState(NeedsMachineAuth)
|
||||||
b.newState(Stopped)
|
b.newState(Stopped)
|
||||||
// TODO(apenwarr): Fill in a more interesting netmap here.
|
// TODO(apenwarr): Fill in a more interesting netmap here.
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tailscale/wireguard-go/wgcfg"
|
"github.com/tailscale/wireguard-go/wgcfg"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
)
|
)
|
||||||
@ -154,6 +155,10 @@ func (h *Handle) StartLoginInteractive() {
|
|||||||
h.b.StartLoginInteractive()
|
h.b.StartLoginInteractive()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handle) Login(token *oauth2.Token) {
|
||||||
|
h.b.Login(token)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handle) Logout() {
|
func (h *Handle) Logout() {
|
||||||
h.b.Logout()
|
h.b.Logout()
|
||||||
}
|
}
|
||||||
|
11
ipn/local.go
11
ipn/local.go
@ -13,6 +13,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tailscale/wireguard-go/wgcfg"
|
"github.com/tailscale/wireguard-go/wgcfg"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
"inet.af/netaddr"
|
"inet.af/netaddr"
|
||||||
"tailscale.com/control/controlclient"
|
"tailscale.com/control/controlclient"
|
||||||
"tailscale.com/ipn/ipnstate"
|
"tailscale.com/ipn/ipnstate"
|
||||||
@ -612,6 +613,16 @@ func (b *LocalBackend) getEngineStatus() EngineStatus {
|
|||||||
return b.engineStatus
|
return b.engineStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Login implements Backend.
|
||||||
|
func (b *LocalBackend) Login(token *oauth2.Token) {
|
||||||
|
b.mu.Lock()
|
||||||
|
b.assertClientLocked()
|
||||||
|
c := b.c
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
c.Login(token, controlclient.LoginInteractive)
|
||||||
|
}
|
||||||
|
|
||||||
// StartLoginInteractive implements Backend. It requests a new
|
// StartLoginInteractive implements Backend. It requests a new
|
||||||
// interactive login from controlclient, unless such a flow is already
|
// interactive login from controlclient, unless such a flow is already
|
||||||
// in progress, in which case StartLoginInteractive attempts to pick
|
// in progress, in which case StartLoginInteractive attempts to pick
|
||||||
|
@ -13,6 +13,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/types/structs"
|
"tailscale.com/types/structs"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
@ -49,6 +50,7 @@ type Command struct {
|
|||||||
Quit *NoArgs
|
Quit *NoArgs
|
||||||
Start *StartArgs
|
Start *StartArgs
|
||||||
StartLoginInteractive *NoArgs
|
StartLoginInteractive *NoArgs
|
||||||
|
Login *oauth2.Token
|
||||||
Logout *NoArgs
|
Logout *NoArgs
|
||||||
SetPrefs *SetPrefsArgs
|
SetPrefs *SetPrefsArgs
|
||||||
RequestEngineStatus *NoArgs
|
RequestEngineStatus *NoArgs
|
||||||
@ -128,6 +130,9 @@ func (bs *BackendServer) GotCommand(cmd *Command) error {
|
|||||||
} else if c := cmd.StartLoginInteractive; c != nil {
|
} else if c := cmd.StartLoginInteractive; c != nil {
|
||||||
bs.b.StartLoginInteractive()
|
bs.b.StartLoginInteractive()
|
||||||
return nil
|
return nil
|
||||||
|
} else if c := cmd.Login; c != nil {
|
||||||
|
bs.b.Login(c)
|
||||||
|
return nil
|
||||||
} else if c := cmd.Logout; c != nil {
|
} else if c := cmd.Logout; c != nil {
|
||||||
bs.b.Logout()
|
bs.b.Logout()
|
||||||
return nil
|
return nil
|
||||||
@ -225,6 +230,10 @@ func (bc *BackendClient) StartLoginInteractive() {
|
|||||||
bc.send(Command{StartLoginInteractive: &NoArgs{}})
|
bc.send(Command{StartLoginInteractive: &NoArgs{}})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc *BackendClient) Login(token *oauth2.Token) {
|
||||||
|
bc.send(Command{Login: token})
|
||||||
|
}
|
||||||
|
|
||||||
func (bc *BackendClient) Logout() {
|
func (bc *BackendClient) Logout() {
|
||||||
bc.send(Command{Logout: &NoArgs{}})
|
bc.send(Command{Logout: &NoArgs{}})
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -177,4 +178,10 @@ func TestClientServer(t *testing.T) {
|
|||||||
|
|
||||||
h.Logout()
|
h.Logout()
|
||||||
flushUntil(NeedsLogin)
|
flushUntil(NeedsLogin)
|
||||||
|
|
||||||
|
h.Login(&oauth2.Token{
|
||||||
|
AccessToken: "google_id_token",
|
||||||
|
TokenType: GoogleIDTokenType,
|
||||||
|
})
|
||||||
|
flushUntil(Running)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user