fix support for SSB URIs on Android Chrome
This commit is contained in:
parent
f6fca892ce
commit
642022cb0a
1
go.mod
1
go.mod
|
@ -20,6 +20,7 @@ require (
|
|||
github.com/mattevans/pwned-passwords v0.3.0
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0
|
||||
github.com/mileusna/useragent v1.0.2 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.1.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351
|
||||
|
|
2
go.sum
2
go.sum
|
@ -150,6 +150,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW
|
|||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0 h1:8E6DrFvII6QR4eJ3PkFvV+lc03P+2qwqTPLm1ax7694=
|
||||
github.com/maxbrunsfeld/counterfeiter/v6 v6.3.0/go.mod h1:fcEyUyXZXoV4Abw8DX0t7wyL8mCDxXyU4iAFZfT3IHw=
|
||||
github.com/mileusna/useragent v1.0.2 h1:DgVKtiPnjxlb73z9bCwgdUvU2nQNQ97uhgfO8l9uz/w=
|
||||
github.com/mileusna/useragent v1.0.2/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc=
|
||||
github.com/miolini/datacounter v0.0.0-20171104152933-fd4e42a1d5e0/go.mod h1:P6fDJzlxN+cWYR09KbE9/ta+Y6JofX9tAUhJpWkWPaM=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
ua "github.com/mileusna/useragent"
|
||||
"github.com/gorilla/mux"
|
||||
"go.mindeco.de/http/render"
|
||||
|
||||
|
@ -159,12 +160,23 @@ func (html aliasHTMLResponder) SendConfirmation(alias roomdb.Alias) {
|
|||
|
||||
// html.multiservAddr
|
||||
ssbURI := url.URL{
|
||||
Scheme: "ssb",
|
||||
Opaque: "experimental",
|
||||
|
||||
Scheme: "ssb",
|
||||
Opaque: "experimental",
|
||||
RawQuery: queryParams.Encode(),
|
||||
}
|
||||
|
||||
// Special treatment for Android Chrome for issue #135
|
||||
// https://github.com/ssb-ngi-pointer/go-ssb-room/issues/135
|
||||
browser := ua.Parse(html.req.UserAgent())
|
||||
if browser.IsAndroid() && browser.IsChrome() {
|
||||
ssbURI = url.URL{
|
||||
Scheme: "intent",
|
||||
Opaque: "//experimental",
|
||||
RawQuery: queryParams.Encode(),
|
||||
Fragment: "Intent;scheme=ssb;package=se.manyver;end;",
|
||||
}
|
||||
}
|
||||
|
||||
err := html.renderer.Render(html.rw, html.req, "alias.tmpl", http.StatusOK, struct {
|
||||
Alias roomdb.Alias
|
||||
|
||||
|
|
|
@ -100,3 +100,63 @@ func TestAliasResolve(t *testing.T) {
|
|||
html, resp = ts.Client.GetHTML(htmlURL)
|
||||
a.Equal(http.StatusInternalServerError, resp.Code)
|
||||
}
|
||||
|
||||
func TestAliasResolveOnAndroidChrome(t *testing.T) {
|
||||
ts := setup(t)
|
||||
|
||||
a := assert.New(t)
|
||||
r := require.New(t)
|
||||
|
||||
var testAlias = roomdb.Alias{
|
||||
ID: 54321,
|
||||
Name: "test-name",
|
||||
Feed: refs.FeedRef{
|
||||
ID: bytes.Repeat([]byte{'F'}, 32),
|
||||
Algo: "test",
|
||||
},
|
||||
Signature: bytes.Repeat([]byte{'S'}, 32),
|
||||
}
|
||||
ts.AliasesDB.ResolveReturns(testAlias, nil)
|
||||
|
||||
// Mimic Android Chrome
|
||||
var uaHeader = make(http.Header)
|
||||
uaHeader.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MOB30H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36")
|
||||
ts.Client.SetHeaders(uaHeader)
|
||||
|
||||
// to construct the /alias/{name} url we need to bypass urlTo
|
||||
// (which builds ?alias=name)
|
||||
routes := router.CompleteApp()
|
||||
|
||||
// default is HTML
|
||||
|
||||
htmlURL, err := routes.Get(router.CompleteAliasResolve).URL("alias", testAlias.Name)
|
||||
r.NoError(err)
|
||||
|
||||
t.Log("resolving", htmlURL.String())
|
||||
html, resp := ts.Client.GetHTML(htmlURL)
|
||||
a.Equal(http.StatusOK, resp.Code)
|
||||
|
||||
a.Equal(testAlias.Name, html.Find("title").Text())
|
||||
|
||||
// ssb-uri in href
|
||||
aliasHref, ok := html.Find("#alias-uri").Attr("href")
|
||||
a.True(ok)
|
||||
aliasURI, err := url.Parse(aliasHref)
|
||||
r.NoError(err)
|
||||
|
||||
a.Equal("intent", aliasURI.Scheme)
|
||||
a.Equal("experimental", aliasURI.Host)
|
||||
|
||||
params := aliasURI.Query()
|
||||
a.Equal("consume-alias", params.Get("action"))
|
||||
a.Equal(testAlias.Name, params.Get("alias"))
|
||||
a.Equal(testAlias.Feed.Ref(), params.Get("userId"))
|
||||
sigData, err := base64.StdEncoding.DecodeString(params.Get("signature"))
|
||||
r.NoError(err)
|
||||
a.Equal(testAlias.Signature, sigData)
|
||||
a.Equal(ts.NetworkInfo.RoomID.Ref(), params.Get("roomId"))
|
||||
a.Equal(ts.NetworkInfo.MultiserverAddress(), params.Get("multiserverAddress"))
|
||||
|
||||
frag := aliasURI.Fragment
|
||||
a.Equal("Intent;scheme=ssb;package=se.manyver;end;", frag)
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/sessions"
|
||||
ua "github.com/mileusna/useragent"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"go.cryptoscope.co/muxrpc/v2"
|
||||
"go.mindeco.de/http/render"
|
||||
|
@ -255,7 +256,7 @@ func (h WithSSBHandler) DecideMethod(w http.ResponseWriter, req *http.Request) {
|
|||
|
||||
// assume server-init sse dance
|
||||
sc := queryVals.Get("sc") // is non-empty when a remote device sends the solution
|
||||
data, err := h.serverInitiated(sc)
|
||||
data, err := h.serverInitiated(sc, req.UserAgent())
|
||||
if err != nil {
|
||||
h.render.Error(w, req, http.StatusInternalServerError, err)
|
||||
return
|
||||
|
@ -348,7 +349,7 @@ type templateData struct {
|
|||
ServerChallenge string
|
||||
}
|
||||
|
||||
func (h WithSSBHandler) serverInitiated(sc string) (templateData, error) {
|
||||
func (h WithSSBHandler) serverInitiated(sc string, userAgent string) (templateData, error) {
|
||||
isSolvingRemotely := true
|
||||
if sc == "" {
|
||||
isSolvingRemotely = false
|
||||
|
@ -363,11 +364,27 @@ func (h WithSSBHandler) serverInitiated(sc string) (templateData, error) {
|
|||
queryParams.Set("sc", sc)
|
||||
queryParams.Set("multiserverAddress", h.netInfo.MultiserverAddress())
|
||||
|
||||
var startAuthURI url.URL
|
||||
startAuthURI := url.URL{
|
||||
Scheme: "ssb",
|
||||
Opaque: "experimental",
|
||||
RawQuery: queryParams.Encode(),
|
||||
}
|
||||
startAuthURI.Scheme = "ssb"
|
||||
startAuthURI.Opaque = "experimental"
|
||||
startAuthURI.RawQuery = queryParams.Encode()
|
||||
|
||||
// Special treatment for Android Chrome for issue #135
|
||||
// https://github.com/ssb-ngi-pointer/go-ssb-room/issues/135
|
||||
browser := ua.Parse(userAgent)
|
||||
if browser.IsAndroid() && browser.IsChrome() {
|
||||
startAuthURI = url.URL{
|
||||
Scheme: "intent",
|
||||
Opaque: "//experimental",
|
||||
RawQuery: queryParams.Encode(),
|
||||
Fragment: "Intent;scheme=ssb;package=se.manyver;end;",
|
||||
}
|
||||
}
|
||||
|
||||
var qrURI string
|
||||
if !isSolvingRemotely {
|
||||
urlTo := web.NewURLTo(router.Auth(h.router), h.netInfo)
|
||||
|
|
|
@ -551,3 +551,52 @@ func TestAuthWithSSBServerInitWrongSolution(t *testing.T) {
|
|||
resp = ts.Client.GetBody(finalizeURL)
|
||||
a.Equal(http.StatusForbidden, resp.Result().StatusCode)
|
||||
}
|
||||
|
||||
func TestAuthWithSSBServerOnAndroidChrome(t *testing.T) {
|
||||
ts := setup(t)
|
||||
a, r := assert.New(t), require.New(t)
|
||||
|
||||
// the keypair for our client
|
||||
testMember := roomdb.Member{ID: 1234}
|
||||
client, err := keys.NewKeyPair(nil)
|
||||
r.NoError(err)
|
||||
testMember.PubKey = client.Feed
|
||||
|
||||
// Mimic Android Chrome
|
||||
var uaHeader = make(http.Header)
|
||||
uaHeader.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MOB30H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36")
|
||||
ts.Client.SetHeaders(uaHeader)
|
||||
|
||||
// setup the mocked database
|
||||
ts.MembersDB.GetByFeedReturns(testMember, nil)
|
||||
|
||||
// prepare the url
|
||||
signInStartURL := ts.URLTo(router.AuthWithSSBLogin,
|
||||
"cid", client.Feed.Ref(),
|
||||
)
|
||||
r.NotNil(signInStartURL)
|
||||
|
||||
html, resp := ts.Client.GetHTML(signInStartURL)
|
||||
if !a.Equal(http.StatusOK, resp.Code, "wrong HTTP status code for dashboard") {
|
||||
t.Log(html.Find("body").Text())
|
||||
}
|
||||
|
||||
serverChallenge, has := html.Find("#challenge").Attr("data-sc")
|
||||
a.True(has, "should have server challenge")
|
||||
a.NotEqual("", serverChallenge)
|
||||
|
||||
ssbURI, has := html.Find("#start-auth-uri").Attr("href")
|
||||
a.True(has, "should have an Android Intent URI")
|
||||
a.True(strings.HasPrefix(ssbURI, "intent://experimental"), "not an Android Intent URI? %s", ssbURI)
|
||||
|
||||
parsedURI, err := url.Parse(ssbURI)
|
||||
r.NoError(err)
|
||||
a.Equal("intent", parsedURI.Scheme)
|
||||
a.Equal("experimental", parsedURI.Host)
|
||||
|
||||
qry := parsedURI.Query()
|
||||
a.Equal("start-http-auth", qry.Get("action"))
|
||||
|
||||
frag := parsedURI.Fragment
|
||||
a.Equal("Intent;scheme=ssb;package=se.manyver;end;", frag)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"net/url"
|
||||
|
||||
"github.com/gorilla/csrf"
|
||||
ua "github.com/mileusna/useragent"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"go.mindeco.de/http/render"
|
||||
"go.mindeco.de/log/level"
|
||||
|
@ -39,11 +40,7 @@ type inviteHandler struct {
|
|||
deniedKeys roomdb.DeniedKeysService
|
||||
}
|
||||
|
||||
func (h inviteHandler) buildJoinRoomURI(token string) template.URL {
|
||||
var joinRoomURI url.URL
|
||||
joinRoomURI.Scheme = "ssb"
|
||||
joinRoomURI.Opaque = "experimental"
|
||||
|
||||
func (h inviteHandler) buildJoinRoomURI(token string, userAgent string) template.URL {
|
||||
queryVals := make(url.Values)
|
||||
queryVals.Set("action", "claim-http-invite")
|
||||
queryVals.Set("invite", token)
|
||||
|
@ -51,7 +48,23 @@ func (h inviteHandler) buildJoinRoomURI(token string) template.URL {
|
|||
submissionURL := h.urlTo(router.CompleteInviteConsume)
|
||||
queryVals.Set("postTo", submissionURL.String())
|
||||
|
||||
joinRoomURI.RawQuery = queryVals.Encode()
|
||||
joinRoomURI := url.URL{
|
||||
Scheme: "ssb",
|
||||
Opaque: "experimental",
|
||||
RawPath: queryVals.Encode(),
|
||||
}
|
||||
|
||||
// Special treatment for Android Chrome for issue #135
|
||||
// https://github.com/ssb-ngi-pointer/go-ssb-room/issues/135
|
||||
browser := ua.Parse(userAgent)
|
||||
if browser.IsAndroid() && browser.IsChrome() {
|
||||
joinRoomURI = url.URL{
|
||||
Scheme: "intent",
|
||||
Opaque: "//experimental",
|
||||
RawQuery: queryVals.Encode(),
|
||||
Fragment: "Intent;scheme=ssb;package=se.manyver;end;",
|
||||
}
|
||||
}
|
||||
|
||||
return template.URL(joinRoomURI.String())
|
||||
}
|
||||
|
@ -118,7 +131,7 @@ func (h inviteHandler) presentFacadeAsHTML(rw http.ResponseWriter, req *http.Req
|
|||
return nil, fmt.Errorf("failed to find room's description: %w", err)
|
||||
}
|
||||
|
||||
joinRoomURI := h.buildJoinRoomURI(token)
|
||||
joinRoomURI := h.buildJoinRoomURI(token, req.UserAgent())
|
||||
|
||||
fallbackURL := h.urlTo(router.CompleteInviteFacadeFallback, "token", token)
|
||||
|
||||
|
|
|
@ -107,6 +107,60 @@ func TestInviteShowAcceptForm(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestInviteShowAcceptFormOnAndroid(t *testing.T) {
|
||||
ts := setup(t)
|
||||
|
||||
a, r := assert.New(t), require.New(t)
|
||||
|
||||
testToken := "existing-test-token"
|
||||
validAcceptURL := ts.URLTo(router.CompleteInviteFacade, "token", testToken)
|
||||
|
||||
// Mimic Android Chrome
|
||||
var uaHeader = make(http.Header)
|
||||
uaHeader.Set("User-Agent", "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5 Build/MOB30H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.133 Mobile Safari/537.36")
|
||||
ts.Client.SetHeaders(uaHeader)
|
||||
|
||||
// prep the mocked db for http:200
|
||||
fakeExistingInvite := roomdb.Invite{ID: 1234}
|
||||
ts.InvitesDB.GetByTokenReturns(fakeExistingInvite, nil)
|
||||
|
||||
// request the form
|
||||
doc, resp := ts.Client.GetHTML(validAcceptURL)
|
||||
a.Equal(http.StatusOK, resp.Code)
|
||||
|
||||
// check database calls
|
||||
r.EqualValues(1, ts.InvitesDB.GetByTokenCallCount())
|
||||
_, tokenFromArg := ts.InvitesDB.GetByTokenArgsForCall(0)
|
||||
a.Equal(testToken, tokenFromArg)
|
||||
|
||||
webassert.Localized(t, doc, []webassert.LocalizedElement{
|
||||
{"#claim-invite-uri", "InviteFacadeJoin"},
|
||||
{"title", "InviteFacadeTitle"},
|
||||
})
|
||||
|
||||
// ssb-uri in href
|
||||
joinDataHref, ok := doc.Find("#claim-invite-uri").Attr("href")
|
||||
a.True(ok)
|
||||
joinURI, err := url.Parse(joinDataHref)
|
||||
r.NoError(err)
|
||||
|
||||
a.Equal("intent", joinURI.Scheme)
|
||||
a.Equal("experimental", joinURI.Host)
|
||||
|
||||
params := joinURI.Query()
|
||||
a.Equal("claim-http-invite", params.Get("action"))
|
||||
|
||||
inviteParam := params.Get("invite")
|
||||
a.Equal(testToken, inviteParam)
|
||||
|
||||
postTo := params.Get("postTo")
|
||||
expectedConsumeInviteURL := ts.URLTo(router.CompleteInviteConsume)
|
||||
a.Equal(expectedConsumeInviteURL.String(), postTo)
|
||||
|
||||
frag := joinURI.Fragment
|
||||
a.Equal("Intent;scheme=ssb;package=se.manyver;end;", frag)
|
||||
}
|
||||
|
||||
func TestInviteConsumeInviteHTTP(t *testing.T) {
|
||||
ts := setup(t)
|
||||
a, r := assert.New(t), require.New(t)
|
||||
|
|
Loading…
Reference in New Issue