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 4 years ago
parent 33849b0daa
commit ec9bb108c6

@ -35,7 +35,7 @@ android {
}
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.browser:browser:1.2.0"
implementation "androidx.security:security-crypto:1.0.0-rc01"

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

@ -33,8 +33,10 @@ var (
// instances being created.
onPeerCreated = make(chan jni.Object)
// onPeerDestroyed receives new global instances of Java Peer
// instances about to be destroyed
// instances about to be destroyed.
onPeerDestroyed = make(chan jni.Object)
// onGoogleToken receives google ID tokens.
onGoogleToken = make(chan string)
)
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
func Java_com_tailscale_ipn_Peer_onSignin(env *C.JNIEnv, this C.jobject) {
// TODO(eliasnaur)
func Java_com_tailscale_ipn_Peer_onSignin(env *C.JNIEnv, this C.jobject, idToken C.jstring) {
jenv := jni.EnvFor(uintptr(unsafe.Pointer(env)))
tok := jni.GoString(jenv, jni.String(idToken))
onGoogleToken <- tok
}
//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"
)
//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 {
jvm jni.JVM
@ -84,8 +84,14 @@ type SearchEvent struct {
type ReauthEvent struct{}
type GoogleAuthEvent 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"
func main() {
@ -218,6 +224,9 @@ func (a *App) runBackend() error {
alarm(a.notifyExpiry(service, m.Expiry))
}
}
case tok := <-onGoogleToken:
// TODO(elias.naur)
_ = tok
case <-alarmChan:
if m := state.NetworkMap; m != nil && service != 0 {
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)
case CopyEvent:
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:
state.query = strings.ToLower(e.Query)
a.updateState(peer, state)

@ -33,6 +33,8 @@ import (
_ "image/png"
)
const enableGoogleSignin = false
type UI struct {
theme *material.Theme
store *stateStore
@ -43,7 +45,11 @@ type UI struct {
enabled widget.Bool
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
peers []widget.Clickable
@ -74,6 +80,7 @@ type UI struct {
search *widget.Icon
more *widget.Icon
logo paint.ImageOp
google paint.ImageOp
}
events []UIEvent
@ -94,6 +101,7 @@ const (
headerColor = 0x496495
infoColor = 0x3a517b
white = 0xffffff
signinColor = 0xe1e3e4
)
const (
@ -122,6 +130,14 @@ func newUI(store *stateStore) (*UI, error) {
if err != nil {
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)
if err != nil {
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.more = moreIcon
ui.icons.logo = paint.NewImageOp(logo)
ui.icons.google = paint.NewImageOp(google)
ui.icons.more.Color = rgb(white)
ui.icons.search.Color = ui.theme.Color.Hint
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{})
}
@ -289,12 +310,47 @@ func (d *Dismiss) Dismissed(gtx layout.Context) bool {
// layoutSignIn lays out the sign in button(s).
func (ui *UI) layoutSignIn(gtx layout.Context) layout.Dimensions {
return layout.Inset{Top: unit.Dp(48)}.Layout(gtx, func(gtx C) D {
return layout.Center.Layout(gtx, func(gtx C) D {
signin := material.Button(ui.theme, &ui.signin, "Sign In")
signin.Background = rgb(headerColor)
return signin.Layout(gtx)
})
return layout.Inset{Top: unit.Dp(48), Left: unit.Dp(48), Right: unit.Dp(48)}.Layout(gtx, func(gtx C) D {
return layout.Flex{Axis: layout.Vertical, Alignment: layout.Middle}.Layout(gtx,
layout.Rigid(func(gtx C) D {
if !enableGoogleSignin {
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".
layout.Rigid(func(gtx C) D {
return layout.N.Layout(gtx, func(gtx C) D {
img := ui.icons.logo
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)}
return drawImage(gtx, ui.icons.logo, unit.Dp(200))
})
}),
// Terms.
@ -730,6 +779,16 @@ func drawLogo(ops *op.Ops, size int) {
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) {
defer op.Push(ops).Pop()
r2 := radius * .5

@ -4,7 +4,7 @@ go 1.14
require (
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
github.com/go-bindata/go-bindata v3.1.2+incompatible
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/go.mod h1:CYwJpIhpzVfoHpFXGlXjSx9mXMWtHt4XXmZb6RjumRc=
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-20200622162632-3a542cc80cee/go.mod h1:jiUwifN9cRl/zmco43aAqh0aV+s9GbhG13KcD+gEpkU=
gioui.org v0.0.0-20200630184435-6ef1ff7cfbfb h1:+jJBzbEtW03w0+fAfhPCwmN0/8xN1CJ94lbfV2eSKhs=
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/go.mod h1:KrsGUGWoPetiRyuDmOd/GTNCBFi2u4UbESTFXZ5YqXY=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

Loading…
Cancel
Save