Update IndieAuth (w/o PKCE for now)

This commit is contained in:
Jan-Lukas Else 2020-12-09 17:25:09 +01:00
parent 0be61598ef
commit db1c4901c7
8 changed files with 108 additions and 125 deletions

View File

@ -56,7 +56,10 @@ func prepareAppDbStatement(query string) (*sql.Stmt, error) {
dbStatementCacheMutex.Unlock() dbStatementCacheMutex.Unlock()
return stmt, nil return stmt, nil
}) })
return stmt.(*sql.Stmt), err if err != nil {
return nil, err
}
return stmt.(*sql.Stmt), nil
} }
func appDbExec(query string, args ...interface{}) (sql.Result, error) { func appDbExec(query string, args ...interface{}) (sql.Result, error) {

View File

@ -71,6 +71,22 @@ func migrateDb() error {
return err return err
}, },
}, },
&migrator.Migration{
Name: "00006",
Func: func(tx *sql.Tx) error {
_, err := tx.Exec(`
create table indieauthauthnew (time text not null, code text not null, client text not null, redirect text not null, scope text not null);
insert into indieauthauthnew (time, code, client, redirect, scope) select time, code, client, redirect, scope from indieauthauth;
drop table indieauthauth;
alter table indieauthauthnew rename to indieauthauth;
create table indieauthtokennew (time text not null, token text not null, client text not null, scope text not null);
insert into indieauthtokennew (time, token, client, scope) select time, token, client, scope from indieauthtoken;
drop table indieauthtoken;
alter table indieauthtokennew rename to indieauthtoken;
`)
return err
},
},
), ),
) )
if err != nil { if err != nil {

6
go.mod
View File

@ -39,7 +39,7 @@ require (
github.com/pelletier/go-toml v1.8.1 // indirect github.com/pelletier/go-toml v1.8.1 // indirect
github.com/smartystreets/assertions v1.2.0 // indirect github.com/smartystreets/assertions v1.2.0 // indirect
github.com/snabb/sitemap v1.0.0 github.com/snabb/sitemap v1.0.0
github.com/spf13/afero v1.5.0 // indirect github.com/spf13/afero v1.5.1 // indirect
github.com/spf13/cast v1.3.1 github.com/spf13/cast v1.3.1
github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.7.1 github.com/spf13/viper v1.7.1
@ -57,12 +57,12 @@ require (
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 // indirect golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 // indirect
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 // indirect
golang.org/x/mod v0.4.0 // indirect golang.org/x/mod v0.4.0 // indirect
golang.org/x/net v0.0.0-20201207224615-747e23833adb // indirect golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 // indirect
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d // indirect golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d // indirect
golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b // indirect golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b // indirect
golang.org/x/text v0.3.4 // indirect golang.org/x/text v0.3.4 // indirect
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7 // indirect golang.org/x/tools v0.0.0-20201208233053-a543418bbed2 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect

13
go.sum
View File

@ -287,8 +287,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.5.0 h1:8Wb647pxgVlypPIdcDlffCLCHCElBZ1sCF6i85qNvRw= github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg=
github.com/spf13/afero v1.5.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
@ -418,8 +418,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201207224615-747e23833adb h1:xj2oMIbduz83x7tzglytWT7spn6rP+9hvKjTpro6/pM= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11 h1:lwlPPsmjDKK0J6eG6xDWd5XPehI0R024zxjDnw3esPA=
golang.org/x/net v0.0.0-20201207224615-747e23833adb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -460,6 +460,7 @@ golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d h1:MiWWjyhUzZ+jvhZvloX6ZrUsd
golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b h1:a0ErnNnPKmhDyIXQvdZr+Lq8dc8xpMeqkF8y5PgQU4Q= golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b h1:a0ErnNnPKmhDyIXQvdZr+Lq8dc8xpMeqkF8y5PgQU4Q=
golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -496,8 +497,8 @@ golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnf
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7 h1:2OSu5vYyX4LVqZAtqZXnFEcN26SDKIJYlEVIRl1tj8U= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2 h1:vEtypaVub6UvKkiXZ2xx9QIvp9TL7sI7xp7vdi2kezA=
golang.org/x/tools v0.0.0-20201208062317-e652b2f42cc7/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=

View File

@ -109,9 +109,9 @@ func buildHandler() (http.Handler, error) {
// IndieAuth // IndieAuth
r.Route("/indieauth", func(indieauthRouter chi.Router) { r.Route("/indieauth", func(indieauthRouter chi.Router) {
indieauthRouter.Use(middleware.NoCache) indieauthRouter.Use(middleware.NoCache)
indieauthRouter.With(authMiddleware, minifier.Middleware).Get("/", indieAuthAuthGet) indieauthRouter.With(minifier.Middleware).Get("/", indieAuthRequest)
indieauthRouter.With(authMiddleware).Post("/accept", indieAuthAccept) indieauthRouter.With(authMiddleware).Post("/accept", indieAuthAccept)
indieauthRouter.Post("/", indieAuthAuthPost) indieauthRouter.Post("/", indieAuthVerification)
indieauthRouter.Get("/token", indieAuthToken) indieauthRouter.Get("/token", indieAuthToken)
indieauthRouter.Post("/token", indieAuthToken) indieauthRouter.Post("/token", indieAuthToken)
}) })

View File

@ -3,7 +3,6 @@ package main
import ( import (
"context" "context"
"net/http" "net/http"
"net/http/httptest"
"strings" "strings"
) )
@ -18,12 +17,7 @@ func checkIndieAuth(next http.Handler) http.Handler {
http.Error(w, err.Error(), http.StatusUnauthorized) http.Error(w, err.Error(), http.StatusUnauthorized)
return return
} }
if !isAllowedHost(httptest.NewRequest(http.MethodGet, tokenData.Me, nil), appConfig.Server.Domain) { next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "scope", strings.Join(tokenData.Scopes, " "))))
http.Error(w, "Forbidden", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), "scope", strings.Join(tokenData.Scopes, " "))
next.ServeHTTP(w, r.WithContext(ctx))
return return
}) })
} }

View File

@ -14,85 +14,46 @@ import (
) )
// https://www.w3.org/TR/indieauth/ // https://www.w3.org/TR/indieauth/
// https://indieauth.spec.indieweb.org/
type indieAuthData struct { type indieAuthData struct {
Me string ClientID string
ClientID string RedirectURI string
RedirectURI string State string
State string Scopes []string
ResponseType string code string
Scopes []string token string
code string time time.Time
token string
time time.Time
} }
func indieAuthAuthGet(w http.ResponseWriter, r *http.Request) { func indieAuthRequest(w http.ResponseWriter, r *http.Request) {
// Authentication / authorization request // Authorization request
r.ParseForm() r.ParseForm()
data := &indieAuthData{ data := &indieAuthData{
Me: r.Form.Get("me"), ClientID: r.Form.Get("client_id"),
ClientID: r.Form.Get("client_id"), RedirectURI: r.Form.Get("redirect_uri"),
RedirectURI: r.Form.Get("redirect_uri"), State: r.Form.Get("state"),
State: r.Form.Get("state"),
ResponseType: r.Form.Get("response_type"),
} }
if data.ResponseType == "" { if rt := r.Form.Get("response_type"); rt != "code" && rt != "id" && rt != "" {
data.ResponseType = "id" http.Error(w, "response_type must be code", http.StatusBadRequest)
return
} }
if scope := r.Form.Get("scope"); scope != "" { if scope := r.Form.Get("scope"); scope != "" {
data.Scopes = strings.Split(scope, " ") data.Scopes = strings.Split(scope, " ")
} }
if !isValidProfileURL(data.Me) || !isValidProfileURL(data.ClientID) || !isValidProfileURL(data.RedirectURI) { if !isValidProfileURL(data.ClientID) || !isValidProfileURL(data.RedirectURI) {
http.Error(w, "me, client_id and redirect_uri need to by valid URLs", http.StatusBadRequest) http.Error(w, "client_id and redirect_uri need to by valid URLs", http.StatusBadRequest)
return return
} }
if data.State == "" { if data.State == "" {
http.Error(w, "state must not be empty", http.StatusBadRequest) http.Error(w, "state must not be empty", http.StatusBadRequest)
return return
} }
if data.ResponseType != "id" && data.ResponseType != "code" {
http.Error(w, "response_type must be empty or id or code", http.StatusBadRequest)
return
}
// if data.ResponseType == "code" && len(data.Scopes) < 1 {
// http.Error(w, "scope is missing or empty", http.StatusBadRequest)
// return
// }
render(w, "indieauth", &renderData{ render(w, "indieauth", &renderData{
Data: data, Data: data,
}) })
} }
func indieAuthAuthPost(w http.ResponseWriter, r *http.Request) {
// Authentication verification
r.ParseForm()
data := &indieAuthData{
code: r.Form.Get("code"),
ClientID: r.Form.Get("client_id"),
RedirectURI: r.Form.Get("redirect_uri"),
}
valid, err := data.verifyAuthorization(true)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if !valid {
http.Error(w, "Authentication not valid", http.StatusForbidden)
return
}
res := &tokenResponse{
Me: data.Me,
}
w.Header().Add(contentType, contentTypeJSONUTF8)
err = json.NewEncoder(w).Encode(res)
if err != nil {
w.Header().Del(contentType)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func isValidProfileURL(profileURL string) bool { func isValidProfileURL(profileURL string) bool {
u, err := url.Parse(profileURL) u, err := url.Parse(profileURL)
if err != nil { if err != nil {
@ -101,8 +62,6 @@ func isValidProfileURL(profileURL string) bool {
if u.Scheme != "http" && u.Scheme != "https" { if u.Scheme != "http" && u.Scheme != "https" {
return false return false
} }
// Missing: Check path
// Missing: Check single/double dot path
if u.Fragment != "" { if u.Fragment != "" {
return false return false
} }
@ -120,16 +79,14 @@ func indieAuthAccept(w http.ResponseWriter, r *http.Request) {
// Authentication flow // Authentication flow
r.ParseForm() r.ParseForm()
data := &indieAuthData{ data := &indieAuthData{
Me: r.Form.Get("me"), ClientID: r.Form.Get("client_id"),
ClientID: r.Form.Get("client_id"), RedirectURI: r.Form.Get("redirect_uri"),
RedirectURI: r.Form.Get("redirect_uri"), State: r.Form.Get("state"),
State: r.Form.Get("state"), Scopes: r.Form["scopes"],
ResponseType: r.Form.Get("response_type"), time: time.Now(),
Scopes: r.Form["scopes"],
time: time.Now(),
} }
sha := sha1.New() sha := sha1.New()
sha.Write([]byte(data.time.String() + data.Me + data.ClientID)) sha.Write([]byte(data.time.String() + data.ClientID))
data.code = fmt.Sprintf("%x", sha.Sum(nil)) data.code = fmt.Sprintf("%x", sha.Sum(nil))
err := data.saveAuthorization() err := data.saveAuthorization()
if err != nil { if err != nil {
@ -147,6 +104,35 @@ type tokenResponse struct {
ClientID string `json:"client_id,omitempty"` ClientID string `json:"client_id,omitempty"`
} }
func indieAuthVerification(w http.ResponseWriter, r *http.Request) {
// Authorization verification
r.ParseForm()
data := &indieAuthData{
code: r.Form.Get("code"),
ClientID: r.Form.Get("client_id"),
RedirectURI: r.Form.Get("redirect_uri"),
}
valid, err := data.verifyAuthorization()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if !valid {
http.Error(w, "Authentication not valid", http.StatusForbidden)
return
}
res := &tokenResponse{
Me: appConfig.Server.PublicAddress,
}
w.Header().Add(contentType, contentTypeJSONUTF8)
err = json.NewEncoder(w).Encode(res)
if err != nil {
w.Header().Del(contentType)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func indieAuthToken(w http.ResponseWriter, r *http.Request) { func indieAuthToken(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet { if r.Method == http.MethodGet {
// Token verification // Token verification
@ -157,7 +143,7 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
} }
res := &tokenResponse{ res := &tokenResponse{
Scope: strings.Join(data.Scopes, " "), Scope: strings.Join(data.Scopes, " "),
Me: data.Me, Me: appConfig.Server.PublicAddress,
ClientID: data.ClientID, ClientID: data.ClientID,
} }
w.Header().Add(contentType, contentTypeJSONUTF8) w.Header().Add(contentType, contentTypeJSONUTF8)
@ -182,9 +168,8 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
code: r.Form.Get("code"), code: r.Form.Get("code"),
ClientID: r.Form.Get("client_id"), ClientID: r.Form.Get("client_id"),
RedirectURI: r.Form.Get("redirect_uri"), RedirectURI: r.Form.Get("redirect_uri"),
Me: r.Form.Get("me"),
} }
valid, err := data.verifyAuthorization(false) valid, err := data.verifyAuthorization()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
@ -199,7 +184,7 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
} }
data.time = time.Now() data.time = time.Now()
sha := sha1.New() sha := sha1.New()
sha.Write([]byte(data.time.String() + data.Me + data.ClientID)) sha.Write([]byte(data.time.String() + data.ClientID))
data.token = fmt.Sprintf("%x", sha.Sum(nil)) data.token = fmt.Sprintf("%x", sha.Sum(nil))
err = data.saveToken() err = data.saveToken()
if err != nil { if err != nil {
@ -210,7 +195,7 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
TokenType: "Bearer", TokenType: "Bearer",
AccessToken: data.token, AccessToken: data.token,
Scope: strings.Join(data.Scopes, " "), Scope: strings.Join(data.Scopes, " "),
Me: data.Me, Me: appConfig.Server.PublicAddress,
} }
w.Header().Add(contentType, contentTypeJSONUTF8) w.Header().Add(contentType, contentTypeJSONUTF8)
err = json.NewEncoder(w).Encode(res) err = json.NewEncoder(w).Encode(res)
@ -227,38 +212,25 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
} }
func (data *indieAuthData) saveAuthorization() (err error) { func (data *indieAuthData) saveAuthorization() (err error) {
_, err = appDbExec("insert into indieauthauth (time, code, me, client, redirect, scope) values (?, ?, ?, ?, ?, ?)", data.time.Unix(), data.code, data.Me, data.ClientID, data.RedirectURI, strings.Join(data.Scopes, " ")) _, err = appDbExec("insert into indieauthauth (time, code, client, redirect, scope) values (?, ?, ?, ?, ?)", data.time.Unix(), data.code, data.ClientID, data.RedirectURI, strings.Join(data.Scopes, " "))
return return
} }
func (data *indieAuthData) verifyAuthorization(authentication bool) (valid bool, err error) { func (data *indieAuthData) verifyAuthorization() (valid bool, err error) {
// code valid for 600 seconds // code valid for 600 seconds
if !authentication { row, err := appDbQueryRow("select code, client, redirect, scope from indieauthauth where time >= ? and code = ? and client = ? and redirect = ?", time.Now().Unix()-600, data.code, data.ClientID, data.RedirectURI)
row, err := appDbQueryRow("select code, me, client, redirect, scope from indieauthauth where time >= ? and code = ? and me = ? and client = ? and redirect = ?", time.Now().Unix()-600, data.code, data.Me, data.ClientID, data.RedirectURI) if err != nil {
if err != nil { return false, err
return false, err }
} scope := ""
scope := "" err = row.Scan(&data.code, &data.ClientID, &data.RedirectURI, &scope)
err = row.Scan(&data.code, &data.Me, &data.ClientID, &data.RedirectURI, &scope) if err == sql.ErrNoRows {
if err == sql.ErrNoRows { return false, nil
return false, nil } else if err != nil {
} else if err != nil { return false, err
return false, err }
} if scope != "" {
if scope != "" { data.Scopes = strings.Split(scope, " ")
data.Scopes = strings.Split(scope, " ")
}
} else {
row, err := appDbQueryRow("select code, me, client, redirect from indieauthauth where time >= ? and code = ? and client = ? and redirect = ?", time.Now().Unix()-600, data.code, data.ClientID, data.RedirectURI)
if err != nil {
return false, err
}
err = row.Scan(&data.code, &data.Me, &data.ClientID, &data.RedirectURI)
if err == sql.ErrNoRows {
return false, nil
} else if err != nil {
return false, err
}
} }
valid = true valid = true
_, err = appDbExec("delete from indieauthauth where code = ? or time < ?", data.code, time.Now().Unix()-600) _, err = appDbExec("delete from indieauthauth where code = ? or time < ?", data.code, time.Now().Unix()-600)
@ -267,7 +239,7 @@ func (data *indieAuthData) verifyAuthorization(authentication bool) (valid bool,
} }
func (data *indieAuthData) saveToken() (err error) { func (data *indieAuthData) saveToken() (err error) {
_, err = appDbExec("insert into indieauthtoken (time, token, me, client, scope) values (?, ?, ?, ?, ?)", data.time.Unix(), data.token, data.Me, data.ClientID, strings.Join(data.Scopes, " ")) _, err = appDbExec("insert into indieauthtoken (time, token, client, scope) values (?, ?, ?, ?)", data.time.Unix(), data.token, data.ClientID, strings.Join(data.Scopes, " "))
return return
} }
@ -276,13 +248,13 @@ func verifyIndieAuthToken(token string) (data *indieAuthData, err error) {
data = &indieAuthData{ data = &indieAuthData{
Scopes: []string{}, Scopes: []string{},
} }
row, err := appDbQueryRow("select time, token, me, client, scope from indieauthtoken where token = @token", sql.Named("token", token)) row, err := appDbQueryRow("select time, token, client, scope from indieauthtoken where token = @token", sql.Named("token", token))
if err != nil { if err != nil {
return nil, err return nil, err
} }
timeString := "" timeString := ""
scope := "" scope := ""
err = row.Scan(&timeString, &data.token, &data.Me, &data.ClientID, &scope) err = row.Scan(&timeString, &data.token, &data.ClientID, &scope)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return nil, errors.New("token not found") return nil, errors.New("token not found")
} else if err != nil { } else if err != nil {

View File

@ -15,14 +15,11 @@
{{ end }} {{ end }}
</ul> </ul>
{{ end }} {{ end }}
<p><strong>me:</strong> {{ .Data.Me }}</p>
<p><strong>client_id:</strong> {{ .Data.ClientID }}</p> <p><strong>client_id:</strong> {{ .Data.ClientID }}</p>
<p><strong>redirect_uri:</strong> {{ .Data.RedirectURI }}</p> <p><strong>redirect_uri:</strong> {{ .Data.RedirectURI }}</p>
<input type="hidden" name="redirect_uri" value="{{ .Data.RedirectURI }}"> <input type="hidden" name="redirect_uri" value="{{ .Data.RedirectURI }}">
<input type="hidden" name="state" value="{{ .Data.State }}"> <input type="hidden" name="state" value="{{ .Data.State }}">
<input type="hidden" id="client_id" name="client_id" value="{{ .Data.ClientID }}"> <input type="hidden" id="client_id" name="client_id" value="{{ .Data.ClientID }}">
<input type="hidden" name="me" value="{{ .Data.Me }}">
<input type="hidden" name="response_type" value="{{ .Data.ResponseType }}">
<input type="submit" value="{{ string .Blog.Lang "authenticate" }}"> <input type="submit" value="{{ string .Blog.Lang "authenticate" }}">
</form> </form>
</div> </div>