From 500914ae62e0677789d7c78ddd32cc2d72362826 Mon Sep 17 00:00:00 2001 From: Thomas Maier Date: Sat, 27 Aug 2022 06:49:37 +0200 Subject: [PATCH 1/2] update x/sys to support go 1.18 on darwin --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3ee0746f..bcacfaa1 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/stretchr/testify v1.5.1 github.com/unidoc/unipdf/v3 v3.6.1 golang.org/x/image v0.0.0-20200119044424-58c23975cae1 // indirect - golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect + golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index f2f62867..504e6839 100644 --- a/go.sum +++ b/go.sum @@ -74,6 +74,8 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= From 3e0fb6257d3167cf7a43671bc648220cf557261f Mon Sep 17 00:00:00 2001 From: Thomas Maier Date: Sat, 27 Aug 2022 09:54:43 +0200 Subject: [PATCH 2/2] make api(/auth) suitable to be used as module --- README.md | 3 ++- api/auth.go | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d01f680d..ceaab0f3 100644 --- a/README.md +++ b/README.md @@ -208,4 +208,5 @@ rMAPI will set the exit code to `0` if the command succeedes, or `1` if it fails - `RMAPI_THUMBNAILS`: generate a thumbnail of the first page of a pdf document - `RMAPI_AUTH`: override the default authorization url - `RMAPI_DOC`: override the default document storage url -- `RMAPI_HOST`: override all urls +- `RMAPI_HOST`: override all urls +- `RMAPI_DEVICE_CODE`: one-time code for non-interactive usage diff --git a/api/auth.go b/api/auth.go index 42e64ceb..18474667 100644 --- a/api/auth.go +++ b/api/auth.go @@ -5,7 +5,9 @@ import ( "fmt" "os" "strings" + "time" + "github.com/golang-jwt/jwt" "github.com/juruen/rmapi/config" "github.com/juruen/rmapi/log" "github.com/juruen/rmapi/model" @@ -23,13 +25,20 @@ func AuthHttpCtx(reAuth, nonInteractive bool) *transport.HttpClientCtx { httpClientCtx := transport.CreateHttpClientCtx(authTokens) if authTokens.DeviceToken == "" { + var oneTimeCode string if nonInteractive { - log.Error.Fatal("missing token, not asking, aborting") + if code, ok := os.LookupEnv("RMAPI_DEVICE_CODE"); ok && len(code) == 8 { + oneTimeCode = code + } else { + log.Error.Fatal("missing token, not asking, aborting") + } + } else { + oneTimeCode = readCode() } - deviceToken, err := newDeviceToken(&httpClientCtx, readCode()) + deviceToken, err := newDeviceToken(&httpClientCtx, oneTimeCode) if err != nil { - log.Error.Fatal("failed to crete device token from on-time code") + log.Error.Fatal("failed to create device token from on-time code") } log.Trace.Println("device token", deviceToken) @@ -40,7 +49,7 @@ func AuthHttpCtx(reAuth, nonInteractive bool) *transport.HttpClientCtx { config.SaveTokens(configPath, authTokens) } - if authTokens.UserToken == "" || reAuth { + if authTokens.UserToken == "" || reAuth || userTokenExpires(authTokens.UserToken) { userToken, err := newUserToken(&httpClientCtx) if err == transport.UnAuthorizedError { @@ -77,6 +86,26 @@ func readCode() string { return code } +func userTokenExpires(token string) bool { + // if there are parsing errors or the registered claim 'exp' is missing/invalid, consider the token to be expired + // TODO: check if the v1 API JWT's contain the exp claim... if not, return false for the above mentioned cases + p := jwt.Parser{} + res, _, err := p.ParseUnverified(token, jwt.MapClaims{}) + if err != nil { + return true + } + claims := res.Claims.(jwt.MapClaims) + if _, ok := claims["exp"]; !ok { + return true + } + exp := time.Unix(int64(claims["exp"].(float64)), 0).UTC() + // add 1 hour to the actual time, assuming the max. session duration of AuthHttpCtx is 1 hour... + if time.Now().UTC().Add(time.Hour).After(exp) { + return true + } + return false +} + func newDeviceToken(http *transport.HttpClientCtx, code string) (string, error) { uuid, err := uuid.NewV4()