cmd/tailscale: add Google sign-in button

Hidden behind a flag pending support from the Tailscale client.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
pull/3/head
Elias Naur 6 years ago
parent 33849b0daa
commit ec9bb108c6

@ -35,7 +35,7 @@ android {
} }
dependencies { dependencies {
//implementation 'com.google.android.gms:play-services-auth:18.0.0' implementation 'com.google.android.gms:play-services-auth:18.0.0'
implementation "androidx.core:core:1.2.0" implementation "androidx.core:core:1.2.0"
implementation "androidx.browser:browser:1.2.0" implementation "androidx.browser:browser:1.2.0"
implementation "androidx.security:security-crypto:1.0.0-rc01" implementation "androidx.security:security-crypto:1.0.0-rc01"

@ -19,24 +19,23 @@ import android.webkit.WebViewClient;
import androidx.browser.customtabs.CustomTabsIntent; import androidx.browser.customtabs.CustomTabsIntent;
/*import com.google.android.gms.auth.api.signin.GoogleSignIn; import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount; import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInClient; import com.google.android.gms.auth.api.signin.GoogleSignInClient;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;*/ import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
public class Peer extends Fragment { public class Peer extends Fragment {
//private final static int REQUEST_SIGNIN = 1001; private final static int REQUEST_SIGNIN = 1001;
private final static int REQUEST_PREPARE_VPN = 1002; private final static int REQUEST_PREPARE_VPN = 1002;
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) { switch (requestCode) {
/*case REQUEST_SIGNIN: case REQUEST_SIGNIN:
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
GoogleSignInAccount acc = GoogleSignIn.getLastSignedInAccount(getActivity()); GoogleSignInAccount acc = GoogleSignIn.getLastSignedInAccount(getActivity());
android.util.Log.i("gio", "Account: " + acc.getId()); onSignin(acc.getIdToken());
onSignin();
return; return;
}*/ }
case REQUEST_PREPARE_VPN: case REQUEST_PREPARE_VPN:
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
onVPNPrepared(); onVPNPrepared();
@ -56,13 +55,15 @@ public class Peer extends Fragment {
super.onDestroy(); super.onDestroy();
} }
/*public void googleSignIn() { public void googleSignIn(String serverOAuthID) {
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(serverOAuthID)
.requestEmail()
.build(); .build();
GoogleSignInClient client = GoogleSignIn.getClient(getActivity(), gso); GoogleSignInClient client = GoogleSignIn.getClient(getActivity(), gso);
Intent signInIntent = client.getSignInIntent(); Intent signInIntent = client.getSignInIntent();
startActivityForResult(signInIntent, REQUEST_SIGNIN); startActivityForResult(signInIntent, REQUEST_SIGNIN);
}*/ }
public void prepareVPN() { public void prepareVPN() {
Intent intent = VpnService.prepare(getActivity()); Intent intent = VpnService.prepare(getActivity());
@ -83,6 +84,6 @@ public class Peer extends Fragment {
private native void fragmentCreated(); private native void fragmentCreated();
private native void fragmentDestroyed(); private native void fragmentDestroyed();
private native void onSignin(); private native void onSignin(String idToken);
private native void onVPNPrepared(); private native void onVPNPrepared();
} }

@ -33,8 +33,10 @@ var (
// instances being created. // instances being created.
onPeerCreated = make(chan jni.Object) onPeerCreated = make(chan jni.Object)
// onPeerDestroyed receives new global instances of Java Peer // onPeerDestroyed receives new global instances of Java Peer
// instances about to be destroyed // instances about to be destroyed.
onPeerDestroyed = make(chan jni.Object) onPeerDestroyed = make(chan jni.Object)
// onGoogleToken receives google ID tokens.
onGoogleToken = make(chan string)
) )
var ( var (
@ -66,8 +68,10 @@ func Java_com_tailscale_ipn_Peer_onVPNPrepared(env *C.JNIEnv, this C.jobject) {
} }
//export Java_com_tailscale_ipn_Peer_onSignin //export Java_com_tailscale_ipn_Peer_onSignin
func Java_com_tailscale_ipn_Peer_onSignin(env *C.JNIEnv, this C.jobject) { func Java_com_tailscale_ipn_Peer_onSignin(env *C.JNIEnv, this C.jobject, idToken C.jstring) {
// TODO(eliasnaur) jenv := jni.EnvFor(uintptr(unsafe.Pointer(env)))
tok := jni.GoString(jenv, jni.String(idToken))
onGoogleToken <- tok
} }
//export Java_com_tailscale_ipn_IPNService_connect //export Java_com_tailscale_ipn_IPNService_connect

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long

@ -23,7 +23,7 @@ import (
"tailscale.com/wgengine/router" "tailscale.com/wgengine/router"
) )
//go:generate go run github.com/go-bindata/go-bindata/go-bindata -nocompress -o logo.go tailscale.png //go:generate go run github.com/go-bindata/go-bindata/go-bindata -nocompress -o logo.go tailscale.png google.png
type App struct { type App struct {
jvm jni.JVM jvm jni.JVM
@ -84,8 +84,14 @@ type SearchEvent struct {
type ReauthEvent struct{} type ReauthEvent struct{}
type GoogleAuthEvent struct{}
type LogoutEvent struct{} type LogoutEvent struct{}
// serverOAuthID is the OAuth ID of the tailscale-android server, used
// by GoogleSignInOptions.Builder.requestIdToken.
const serverOAuthID = "744055068597-hv4opg0h7vskq1hv37nq3u26t8c15qk0.apps.googleusercontent.com"
const enabledKey = "ipn_enabled" const enabledKey = "ipn_enabled"
func main() { func main() {
@ -218,6 +224,9 @@ func (a *App) runBackend() error {
alarm(a.notifyExpiry(service, m.Expiry)) alarm(a.notifyExpiry(service, m.Expiry))
} }
} }
case tok := <-onGoogleToken:
// TODO(elias.naur)
_ = tok
case <-alarmChan: case <-alarmChan:
if m := state.NetworkMap; m != nil && service != 0 { if m := state.NetworkMap; m != nil && service != 0 {
alarm(a.notifyExpiry(service, m.Expiry)) alarm(a.notifyExpiry(service, m.Expiry))
@ -569,6 +578,14 @@ func (a *App) processUIEvents(w *app.Window, events []UIEvent, peer jni.Object,
a.request(e) a.request(e)
case CopyEvent: case CopyEvent:
w.WriteClipboard(e.Text) w.WriteClipboard(e.Text)
case GoogleAuthEvent:
err := jni.Do(a.jvm, func(env jni.Env) error {
sid := jni.JavaString(env, serverOAuthID)
return a.callVoidMethod(peer, "googleSignIn", "(Ljava/lang/String;)V", jni.Value(sid))
})
if err != nil {
fatalErr(err)
}
case SearchEvent: case SearchEvent:
state.query = strings.ToLower(e.Query) state.query = strings.ToLower(e.Query)
a.updateState(peer, state) a.updateState(peer, state)

@ -33,6 +33,8 @@ import (
_ "image/png" _ "image/png"
) )
const enableGoogleSignin = false
type UI struct { type UI struct {
theme *material.Theme theme *material.Theme
store *stateStore store *stateStore
@ -43,7 +45,11 @@ type UI struct {
enabled widget.Bool enabled widget.Bool
search widget.Editor search widget.Editor
signin widget.Clickable // webSigin is the button for the web-based sign-in flow.
webSignin widget.Clickable
// googleSignin is the button for native Google Sign-in
googleSignin widget.Clickable
self widget.Clickable self widget.Clickable
peers []widget.Clickable peers []widget.Clickable
@ -74,6 +80,7 @@ type UI struct {
search *widget.Icon search *widget.Icon
more *widget.Icon more *widget.Icon
logo paint.ImageOp logo paint.ImageOp
google paint.ImageOp
} }
events []UIEvent events []UIEvent
@ -94,6 +101,7 @@ const (
headerColor = 0x496495 headerColor = 0x496495
infoColor = 0x3a517b infoColor = 0x3a517b
white = 0xffffff white = 0xffffff
signinColor = 0xe1e3e4
) )
const ( const (
@ -122,6 +130,14 @@ func newUI(store *stateStore) (*UI, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
googleData, err := googlePngBytes()
if err != nil {
return nil, err
}
google, _, err := image.Decode(bytes.NewReader(googleData))
if err != nil {
return nil, err
}
face, err := opentype.Parse(robotoregular.TTF) face, err := opentype.Parse(robotoregular.TTF)
if err != nil { if err != nil {
panic(fmt.Sprintf("failed to parse font: %v", err)) panic(fmt.Sprintf("failed to parse font: %v", err))
@ -135,6 +151,7 @@ func newUI(store *stateStore) (*UI, error) {
ui.icons.search = searchIcon ui.icons.search = searchIcon
ui.icons.more = moreIcon ui.icons.more = moreIcon
ui.icons.logo = paint.NewImageOp(logo) ui.icons.logo = paint.NewImageOp(logo)
ui.icons.google = paint.NewImageOp(google)
ui.icons.more.Color = rgb(white) ui.icons.more.Color = rgb(white)
ui.icons.search.Color = ui.theme.Color.Hint ui.icons.search.Color = ui.theme.Color.Hint
ui.root.Axis = layout.Vertical ui.root.Axis = layout.Vertical
@ -169,7 +186,11 @@ func (ui *UI) layout(gtx layout.Context, sysIns system.Insets, state *clientStat
} }
} }
if ui.signin.Clicked() { if ui.googleSignin.Clicked() {
ui.events = append(ui.events, GoogleAuthEvent{})
}
if ui.webSignin.Clicked() {
ui.events = append(ui.events, ReauthEvent{}) ui.events = append(ui.events, ReauthEvent{})
} }
@ -289,12 +310,47 @@ func (d *Dismiss) Dismissed(gtx layout.Context) bool {
// layoutSignIn lays out the sign in button(s). // layoutSignIn lays out the sign in button(s).
func (ui *UI) layoutSignIn(gtx layout.Context) layout.Dimensions { func (ui *UI) layoutSignIn(gtx layout.Context) layout.Dimensions {
return layout.Inset{Top: unit.Dp(48)}.Layout(gtx, func(gtx C) D { return layout.Inset{Top: unit.Dp(48), Left: unit.Dp(48), Right: unit.Dp(48)}.Layout(gtx, func(gtx C) D {
return layout.Center.Layout(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx,
signin := material.Button(ui.theme, &ui.signin, "Sign In") layout.Rigid(func(gtx C) D {
signin.Background = rgb(headerColor) if !enableGoogleSignin {
return signin.Layout(gtx) return D{}
}
signin := material.ButtonLayout(ui.theme, &ui.googleSignin)
//signin.Background = rgb(headerColor)
signin.Background = rgb(signinColor)
return layout.Inset{Bottom: unit.Dp(16)}.Layout(gtx, func(gtx C) D {
return signin.Layout(gtx, func(gtx C) D {
gtx.Constraints.Max.Y = gtx.Px(unit.Dp(48))
return layout.Flex{Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(func(gtx C) D {
return layout.Inset{Right: unit.Dp(4)}.Layout(gtx, func(gtx C) D {
return drawImage(gtx, ui.icons.google, unit.Dp(16))
}) })
}),
layout.Rigid(func(gtx C) D {
return layout.Inset{Top: unit.Dp(10), Bottom: unit.Dp(10)}.Layout(gtx, func(gtx C) D {
l := material.Body2(ui.theme, "Sign in with Google")
l.Color = ui.theme.Color.Text
return l.Layout(gtx)
})
}),
)
})
})
}),
layout.Rigid(func(gtx C) D {
label := "Sign in with other"
if !enableGoogleSignin {
label = "Sign in"
}
signin := material.Button(ui.theme, &ui.webSignin, label)
signin.Background = rgb(signinColor)
signin.Color = ui.theme.Color.Text
return signin.Layout(gtx)
}),
)
}) })
} }
@ -338,14 +394,7 @@ func (ui *UI) layoutIntro(gtx layout.Context) {
// "tailscale". // "tailscale".
layout.Rigid(func(gtx C) D { layout.Rigid(func(gtx C) D {
return layout.N.Layout(gtx, func(gtx C) D { return layout.N.Layout(gtx, func(gtx C) D {
img := ui.icons.logo return drawImage(gtx, ui.icons.logo, unit.Dp(200))
img.Add(gtx.Ops)
sz := img.Size()
aspect := float32(sz.Y) / float32(sz.X)
w := gtx.Px(unit.Dp(200))
h := int(float32(w)*aspect + .5)
paint.PaintOp{Rect: f32.Rectangle{Max: f32.Pt(float32(w), float32(h))}}.Add(gtx.Ops)
return layout.Dimensions{Size: image.Pt(w, h)}
}) })
}), }),
// Terms. // Terms.
@ -730,6 +779,16 @@ func drawLogo(ops *op.Ops, size int) {
row.Pop() row.Pop()
} }
func drawImage(gtx layout.Context, img paint.ImageOp, size unit.Value) layout.Dimensions {
img.Add(gtx.Ops)
sz := img.Size()
aspect := float32(sz.Y) / float32(sz.X)
w := gtx.Px(size)
h := int(float32(w)*aspect + .5)
paint.PaintOp{Rect: f32.Rectangle{Max: f32.Pt(float32(w), float32(h))}}.Add(gtx.Ops)
return layout.Dimensions{Size: image.Pt(w, h)}
}
func drawDisc(ops *op.Ops, radius float32, col color.RGBA) { func drawDisc(ops *op.Ops, radius float32, col color.RGBA) {
defer op.Push(ops).Pop() defer op.Push(ops).Pop()
r2 := radius * .5 r2 := radius * .5

@ -4,7 +4,7 @@ go 1.14
require ( require (
eliasnaur.com/font v0.0.0-20200617114307-e02d32decb4b eliasnaur.com/font v0.0.0-20200617114307-e02d32decb4b
gioui.org v0.0.0-20200622162632-3a542cc80cee gioui.org v0.0.0-20200630184435-6ef1ff7cfbfb
gioui.org/cmd v0.0.0-20200622185735-5bd0ecea5e43 gioui.org/cmd v0.0.0-20200622185735-5bd0ecea5e43
github.com/go-bindata/go-bindata v3.1.2+incompatible github.com/go-bindata/go-bindata v3.1.2+incompatible
github.com/tailscale/wireguard-go v0.0.0-20200615180905-687c10194779 github.com/tailscale/wireguard-go v0.0.0-20200615180905-687c10194779

@ -3,8 +3,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
eliasnaur.com/font v0.0.0-20200617114307-e02d32decb4b h1:J9r7EuPdhvBTafg34EqrObAm/bDEaDh7LvhKJPGficE= eliasnaur.com/font v0.0.0-20200617114307-e02d32decb4b h1:J9r7EuPdhvBTafg34EqrObAm/bDEaDh7LvhKJPGficE=
eliasnaur.com/font v0.0.0-20200617114307-e02d32decb4b/go.mod h1:CYwJpIhpzVfoHpFXGlXjSx9mXMWtHt4XXmZb6RjumRc= eliasnaur.com/font v0.0.0-20200617114307-e02d32decb4b/go.mod h1:CYwJpIhpzVfoHpFXGlXjSx9mXMWtHt4XXmZb6RjumRc=
gioui.org v0.0.0-20200622101735-5368743478e0/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU= gioui.org v0.0.0-20200622101735-5368743478e0/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU=
gioui.org v0.0.0-20200622162632-3a542cc80cee h1:qJXyQltqYTE3EURAc5ud8alfncvhIHKhsq/ooxdsqMo= gioui.org v0.0.0-20200630184435-6ef1ff7cfbfb h1:+jJBzbEtW03w0+fAfhPCwmN0/8xN1CJ94lbfV2eSKhs=
gioui.org v0.0.0-20200622162632-3a542cc80cee/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU= gioui.org v0.0.0-20200630184435-6ef1ff7cfbfb/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU=
gioui.org/cmd v0.0.0-20200622185735-5bd0ecea5e43 h1:Wj8OoCIw06dNSSSPAAipnmcG7dbFn+7Et9IY37e1HBU= gioui.org/cmd v0.0.0-20200622185735-5bd0ecea5e43 h1:Wj8OoCIw06dNSSSPAAipnmcG7dbFn+7Et9IY37e1HBU=
gioui.org/cmd v0.0.0-20200622185735-5bd0ecea5e43/go.mod h1:KrsGUGWoPetiRyuDmOd/GTNCBFi2u4UbESTFXZ5YqXY= gioui.org/cmd v0.0.0-20200622185735-5bd0ecea5e43/go.mod h1:KrsGUGWoPetiRyuDmOd/GTNCBFi2u4UbESTFXZ5YqXY=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

Loading…
Cancel
Save