1
0
mirror of https://github.com/rclone/rclone.git synced 2025-12-06 00:03:32 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
albertony
f838c2a2f2 jottacloud: add support for MediaMarkt Cloud as a whitelabel service
This was requested in issue #8852, after authentication was already fixed for existing
whitelabels.
2025-10-04 17:52:22 +02:00
albertony
49103c7348 jottacloud: refactor service list from map to slice to get predefined order 2025-10-04 15:26:11 +02:00
albertony
697874e399 jottacloud: added support for traditional oauth authentication also for the main service
This renames whitelabel authentication to traditional authentication and adds support for
the main Jottacloud service also here, as it can be used as an alternative to the
authentication based on personal login token for those who prefer it. Documentation
also adjusted correspondingly, and restructured the authentication section a bit more
since some of the sections that was under standard authentication in reality also
applies to the traditional authentication.
2025-10-04 15:26:11 +02:00
2 changed files with 146 additions and 95 deletions

View File

@@ -17,7 +17,7 @@ import (
"net/url"
"os"
"path"
"sort"
"slices"
"strconv"
"strings"
"time"
@@ -60,7 +60,7 @@ const (
configVersion = 1
defaultTokenURL = "https://id.jottacloud.com/auth/realms/jottacloud/protocol/openid-connect/token"
defaultClientID = "jottacli"
defaultClientID = "jottacli" // Identified as "Jottacloud CLI" in "My logged in devices"
legacyTokenURL = "https://api.jottacloud.com/auth/v1/token"
legacyRegisterURL = "https://api.jottacloud.com/auth/v1/register"
@@ -69,27 +69,29 @@ const (
legacyConfigVersion = 0
)
func getWhitelabelServices() map[string]struct {
name, domain, realm, clientID string
scopes []string
} {
return map[string]struct {
name string
domain string
realm string
clientID string
scopes []string
}{
"telia_se": {"Telia Cloud (Sweden)", "cloud-auth.telia.se", "telia_se", "desktop", []string{"openid", "jotta-default", "offline_access"}},
"telia_no": {"Telia Sky (Norway)", "sky-auth.telia.no", "get", "desktop", []string{"openid", "jotta-default", "offline_access"}},
"tele2": {"Tele2 Cloud (Sweden)", "mittcloud-auth.tele2.se", "comhem", "desktop", []string{"openid", "jotta-default", "offline_access"}},
"onlime": {"Onlime (Denmark)", "cloud-auth.onlime.dk", "onlime_wl", "desktop", []string{"openid", "jotta-default", "offline_access"}},
"elkjop": {"Elkjøp Cloud (Norway)", "cloud.elkjop.no", "elkjop", "desktop", []string{"openid", "jotta-default", "offline_access"}},
"elgiganten_se": {"Elgiganten Cloud (Sweden)", "cloud.elgiganten.se", "elgiganten", "desktop", []string{"openid", "jotta-default", "offline_access"}},
"elgiganten_dk": {"Elgiganten Cloud (Denmark)", "cloud.elgiganten.dk", "elgiganten", "desktop", []string{"openid", "jotta-default", "offline_access"}},
"gigantti": {"Gigantti Cloud (Finland)", "cloud.gigantti.fi", "gigantti", "desktop", []string{"openid", "jotta-default", "offline_access"}},
"elko": {"ELKO Cloud (Iceland)", "cloud.elko.is", "elko", "desktop", []string{"openid", "jotta-default", "offline_access"}},
"letsgo": {"Let's Go Cloud (Germany)", "letsgo.jotta.cloud", "letsgo", "desktop-win", []string{"openid", "offline_access"}},
type service struct {
key string
name string
domain string
realm string
clientID string
scopes []string
}
func getServices() []service {
return []service{
{"jottacloud", "Jottacloud", "id.jottacloud.com", "jottacloud", "desktop", []string{"openid", "jotta-default", "offline_access"}}, // Chose client id "desktop" here, will be identified as "Jottacloud for Desktop" in "My logged in devices", but could have used "jottacli" here as well.
{"elkjop", "Elkjøp Cloud (Norway)", "cloud.elkjop.no", "elkjop", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"elgiganten_dk", "Elgiganten Cloud (Denmark)", "cloud.elgiganten.dk", "elgiganten", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"elgiganten_se", "Elgiganten Cloud (Sweden)", "cloud.elgiganten.se", "elgiganten", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"elko", "ELKO Cloud (Iceland)", "cloud.elko.is", "elko", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"gigantti", "Gigantti Cloud (Finland)", "cloud.gigantti.fi", "gigantti", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"telia_se", "Telia Cloud (Sweden)", "cloud-auth.telia.se", "telia_se", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"telia_no", "Telia Sky (Norway)", "sky-auth.telia.no", "get", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"tele2", "Tele2 Cloud (Sweden)", "mittcloud-auth.tele2.se", "comhem", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"onlime", "Onlime (Denmark)", "cloud-auth.onlime.dk", "onlime_wl", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"mediamarkt", "MediaMarkt Cloud", "mediamarkt.jottacloud.com", "mediamarkt", "desktop", []string{"openid", "jotta-default", "offline_access"}},
{"letsgo", "Let's Go Cloud (Germany)", "letsgo.jotta.cloud", "letsgo", "desktop-win", []string{"openid", "offline_access"}},
}
}
@@ -176,20 +178,32 @@ func Config(ctx context.Context, name string, m configmap.Mapper, conf fs.Config
}
return fs.ConfigChooseExclusiveFixed("auth_type_done", "config_type", `Type of authentication.`, []fs.OptionExample{{
Value: "standard",
Help: "Standard authentication.\nUse this if you're a normal Jottacloud user.",
Help: `Standard authentication.
This is primarily supported by the official service, but may also be supported
by some of the white-label services. It is designed for command-line
applications, and you will be asked to enter a single-use personal login token
which you must manually generate from the account security settings in the
web interface of your service.`,
}, {
Value: "whitelabel",
Help: "Whitelabel authentication.\nUse this if you are using the service offered by a third party such as Telia, Tele2, Onlime, Elkjøp, etc.",
Value: "traditional",
Help: `Traditional authentication.
This is supported by the official service and most of the white-label
services, you will be asked which service to connect to. You need to be on
a machine with an internet-connected web browser.`,
}, {
Value: "legacy",
Help: "Legacy authentication.\nThis is no longer supported by any known services and not recommended for normal users.",
Help: `Legacy authentication.
This is no longer supported by any known services and not recommended used.
You will be asked for your account's username and password.`,
}})
case "auth_type_done":
// Jump to next state according to config chosen
return fs.ConfigGoto(conf.Result)
case "standard": // configure a jottacloud backend using the modern JottaCli token based authentication
m.Set("configVersion", fmt.Sprint(configVersion))
return fs.ConfigInput("standard_token", "config_login_token", "Personal login token.\nGenerate here: https://www.jottacloud.com/web/secure")
return fs.ConfigInput("standard_token", "config_login_token", `Personal login token.
Generate it from the account security settings in the web interface of your
service, for the official service on https://www.jottacloud.com/web/secure.`)
case "standard_token":
loginToken := conf.Result
m.Set(configClientID, defaultClientID)
@@ -206,29 +220,28 @@ func Config(ctx context.Context, name string, m configmap.Mapper, conf fs.Config
return nil, fmt.Errorf("error while saving token: %w", err)
}
return fs.ConfigGoto("choose_device")
case "whitelabel":
whitelabels := getWhitelabelServices()
options := make([]fs.OptionExample, 0, len(whitelabels))
for key, val := range whitelabels {
case "traditional":
services := getServices()
options := make([]fs.OptionExample, 0, len(services))
for _, service := range services {
options = append(options, fs.OptionExample{
Value: key,
Help: val.name,
Value: service.key,
Help: service.name,
})
}
sort.Slice(options, func(i, j int) bool {
return options[i].Help < options[j].Help
})
return fs.ConfigChooseExclusiveFixed("whitelabel_type", "config_whitelabel",
return fs.ConfigChooseExclusiveFixed("traditional_type", "config_traditional",
"White-label service. This decides the domain name to connect to and\nthe authentication configuration to use.",
options)
case "whitelabel_type":
whitelabel, ok := getWhitelabelServices()[conf.Result]
if !ok {
return nil, fmt.Errorf("unexpected whitelabel %q", conf.Result)
case "traditional_type":
services := getServices()
i := slices.IndexFunc(services, func(s service) bool { return s.key == conf.Result })
if i == -1 {
return nil, fmt.Errorf("unexpected service %q", conf.Result)
}
service := services[i]
opts := rest.Opts{
Method: "GET",
RootURL: "https://" + whitelabel.domain + "/auth/realms/" + whitelabel.realm + "/.well-known/openid-configuration",
RootURL: "https://" + service.domain + "/auth/realms/" + service.realm + "/.well-known/openid-configuration",
}
var wellKnown api.WellKnown
srv := rest.NewClient(fshttp.NewClient(ctx))
@@ -237,14 +250,14 @@ func Config(ctx context.Context, name string, m configmap.Mapper, conf fs.Config
return nil, fmt.Errorf("failed to get authentication provider configuration: %w", err)
}
m.Set("configVersion", fmt.Sprint(configVersion))
m.Set(configClientID, whitelabel.clientID)
m.Set(configClientID, service.clientID)
m.Set(configTokenURL, wellKnown.TokenEndpoint)
return oauthutil.ConfigOut("choose_device", &oauthutil.Options{
OAuth2Config: &oauthutil.Config{
AuthURL: wellKnown.AuthorizationEndpoint,
TokenURL: wellKnown.TokenEndpoint,
ClientID: whitelabel.clientID,
Scopes: whitelabel.scopes,
ClientID: service.clientID,
Scopes: service.scopes,
RedirectURL: oauthutil.RedirectLocalhostURL,
},
})

View File

@@ -28,60 +28,86 @@ as described [below](#whitelabel-authentication):
- Onlime
- Onlime (onlime.dk)
- MediaMarkt
- MediaMarkt Cloud (mediamarkt.jottacloud.com)
- Let's Go Cloud (letsgo.jotta.cloud)
Paths are specified as `remote:path`
Paths may be as deep as required, e.g. `remote:directory/subdirectory`.
## Authentication types
## Authentication
Some of the white-label versions uses a different authentication method than the
official service, and you have to choose the correct one when setting up the remote.
### Standard authentication
The standard authentication method used by the official service (jottacloud.com),
as well as some of the white-label services, is based on OAuth 2.0 and OpenID
Connect (OIDC), and requires you to generate a single-use personal login token
from the account security settings in the service's web interface. Log in to your
account, go to "Settings" and then "Security", or use the direct link presented
to you by rclone when configuring the remote:
<https://www.jottacloud.com/web/secure>. Scroll down to the section "Personal login
token", and click the "Generate" button. Note that if you are using a white-label
service you probably can't use the direct link, you need to find the same page in
their dedicated web interface, and also it may be in a different location than
described above.
Authentication in Jottacloud is in general based on OAuth 2.0 and OpenID
Connect (OIDC). There are different variants to choose from, described below.
Some of the variants are only supported by the official service and not
white-label services, so this must be taken into consideration when choosing.
To access your account from multiple instances of rclone, you need to configure
each of them with a separate personal login token. E.g. you create a Jottacloud
remote with rclone in one location, and copy the configuration file to a second
location where you also want to run rclone and access the same remote. Then you
need to replace the token for one of them, using the [config reconnect](https://rclone.org/commands/rclone_config_reconnect/)
command, which requires you to generate a new personal login token and supply
as input. If you do not do this, the token may easily end up being invalidated,
resulting in both instances failing with an error message something along the
lines of:
each of them separately. E.g. you create a Jottacloud remote with rclone in one
location, and copy the configuration file to a second location where you also
want to run rclone and access the same remote. Then you need to replace the
token for one of them, using the [config reconnect](https://rclone.org/commands/rclone_config_reconnect/)
command. For standard authentication (described below) this means you will have
to generate a new personal login token and supply as input. If you do not do
this, the token may easily end up being invalidated, resulting in both
instances failing with an error message something along the lines of:
```text
oauth2: cannot fetch token: 400 Bad Request
Response: {"error":"invalid_grant","error_description":"Stale token"}
```
When this happens, you need to replace the token as described above to be able
to use your remote again.
The background for this is that OAuth tokens from Jottacloud normally have one
hour expiry, after which they will be automatically refreshed by rclone.
Jottacloud will then refresh not only the access token, but also the refresh
token. Any requests using a previous refresh token will be flagged, and lead
to the stale token error. When this happens, you need to replace the token as
described above to be able to use your remote again.
All personal login tokens you have taken into use will be listed in the web
interface under "My logged in devices", and from the right side of that list
you can click the "X" button to revoke individual tokens.
Each time you are granted access with a new token, it will listed in the web
interface under "My logged in devices". From the right side of that list you
can click the "X" button to revoke access. This will effectively disable the
refresh token, which means you will still have access using an existing access
token until that expires, but you will not be able to refresh it.
### Whitelabel authentication
### Standard
Most of the white-label versions uses a slightly different authentication flow,
where it doesn't offer the option of creating a CLI token, and the username
is generated internally. To setup rclone to use one of these, choose white-label
authentication in the setup process, and then select the specific service
in the next step.
This is an OAuth variant designed for command-line applications. It is
primarily supported by the official service (jottacloud.com), but may also be
supported by some of the white-label services. The specific domain name and
endpoint to connect to are found automatically (it is encoded into the supplied
login token, described next).
When configuring a remote, you are asked to enter a single-use personal login
token, which you must manually generate from the account security settings in
the service's web interface. You do not need a web browser on the same machine
like with traditional OAuth, but need to use a web browser somewhere, and be
able to be copy the generated string into your rclone configuration session.
Log in to your account, go to "Settings" and then "Security", or use the direct
link presented to you by rclone when configuring the remote:
<https://www.jottacloud.com/web/secure>. Scroll down to the section "Personal
login token", and click the "Generate" button. Note that if you are using a
white-label service you probably can't use the direct link, you need to find
the same page in their dedicated web interface, and also it may be in a
different location than described above.
When you have successfully authenticated using a personal login token, which
means you have received a proper OAuth token, there will be an entry in the
"My logged in devices" list in the web interface. It will be listed with
application name "Jottacloud CLI".
### Traditional
Jottacloud also supports a more traditional OAuth variant. Most of the
white-label services supports this, and often only this as they do not support
personal login tokens. This method relies on pre-defined domain names and
endpoints, and rclone must therefore explicitly add any white-label services
that should be supported.
When configuring a remote, you must interactively login to an OAuth
authorization web site, and a one-time authorization code are automatically
sent back to rclone, which it uses to request a token.
Note that when setting this up, you need to be on a machine with an
internet-connected web browser. If you need it on a machine where this is not
@@ -90,14 +116,18 @@ and copy it from there. The jottacloud backend does not support the
`rclone authorize` command. See the [remote setup docs](/remote_setup) for
details.
### Legacy authentication
When you have successfully authenticated, there will be an entry in the
"My logged in devices" list in the web interface. It will typically be listed
with application name "Jottacloud for Desktop" or similar (it depends on the
white-label service configuration).
Originally Jottacloud used an older authentication method, not based on OpenID
Connect, which required the username and password to be specified. Since
Jottacloud migrated to the newer method, handled by the standard authentication,
some white-label versions (those from Elkjøp) still used the legacy method for
a long time. Currently there are no known uses of this, it is still supported
by rclone, but the support will be removed in a future version.
### Legacy
Originally Jottacloud used an OAuth variant which required your account's
username and password to be specified. When Jottacloud migrated to the newer
methods, some white-label versions (those from Elkjøp) still used this legacy
method for a long time. Currently there are no known uses of this, it is still
supported by rclone, but the support will be removed in a future version.
## Configuration
@@ -151,19 +181,27 @@ Type of authentication.
Choose a number from below, or type in an existing value of type string.
Press Enter for the default (standard).
/ Standard authentication.
1 | Use this if you're a normal Jottacloud user.
| This is primarily supported by the official service, but may also be supported
| by some of the white-label services. It is designed for command-line
1 | applications, and you will be asked to enter a single-use personal login token
| which you must manually generate from the account security settings in the
| web interface of your service.
\ (standard)
/ Whitelabel authentication.
2 | Use this if you are using the service offered by a third party such as Telia, Tele2, Onlime, Elkjøp, etc.
\ (whitelabel)
/ Traditional authentication.
| This is supported by the official service and most of the white-label
2 | services, you will be asked which service to connect to. You need to be on
| a machine with an internet-connected web browser.
\ (traditional)
/ Legacy authentication.
3 | This is no longer supported by any known services and not recommended for normal users.
3 | This is no longer supported by any known services and not recommended used.
| You will be asked for your account's username and password.
\ (legacy)
config_type> 1
Option config_login_token.
Personal login token.
Generate here: https://www.jottacloud.com/web/secure
Generate it from the account security settings in the web interface of your
service, for the official service on https://www.jottacloud.com/web/secure.
Enter a value.
config_login_token> <your token here>