drive, googlephotos: warn in config wizard before using the shared client_id #9580
rclone's shared Google Drive and Google Photos client_id is being retired and will stop working during 2026. When creating a new remote that would use it, the config wizard now warns the user and asks the user to enter their own client_id and secret instead. Service account and environment auth are unaffected as they don't use the shared client_id. See: https://forum.rclone.org/t/google-drive-and-google-photos-users-action-required/54005
This commit is contained in:
+32
-10
@@ -208,7 +208,10 @@ func driveOAuthOptions() []fs.Option {
|
||||
opts := []fs.Option{}
|
||||
for _, opt := range oauthutil.SharedOptions {
|
||||
if opt.Name == config.ConfigClientID {
|
||||
opt.Help = "Google Application Client Id\nSetting your own is recommended.\nSee https://rclone.org/drive/#making-your-own-client-id for how to create your own.\nIf you leave this blank, it will use an internal key which is low performance."
|
||||
opt.Help = "Google Application Client Id\nLeave blank to use rclone's shared client_id, or if you are using a service account.\nThe shared client_id is being retired and will stop working during 2026, so creating your own is now strongly recommended.\nSee https://rclone.org/drive/#making-your-own-client-id for how to create your own."
|
||||
}
|
||||
if opt.Name == config.ConfigClientSecret {
|
||||
opt.Help = "Google Application Client Secret\nLeave blank to use rclone's shared client_id, or if you are using a service account.\nIf you created your own client_id then enter its client secret here."
|
||||
}
|
||||
opts = append(opts, opt)
|
||||
}
|
||||
@@ -222,7 +225,7 @@ func init() {
|
||||
Description: "Google Drive",
|
||||
NewFs: NewFs,
|
||||
CommandHelp: commandHelp,
|
||||
Config: func(ctx context.Context, name string, m configmap.Mapper, config fs.ConfigIn) (*fs.ConfigOut, error) {
|
||||
Config: func(ctx context.Context, name string, m configmap.Mapper, configIn fs.ConfigIn) (*fs.ConfigOut, error) {
|
||||
// Parse config into Options struct
|
||||
opt := new(Options)
|
||||
err := configstruct.Set(m, opt)
|
||||
@@ -230,7 +233,7 @@ func init() {
|
||||
return nil, fmt.Errorf("couldn't parse config into struct: %w", err)
|
||||
}
|
||||
|
||||
switch config.State {
|
||||
switch configIn.State {
|
||||
case "":
|
||||
// Fill in the scopes
|
||||
driveConfig.Scopes = driveScopes(opt.Scope)
|
||||
@@ -241,24 +244,43 @@ func init() {
|
||||
}
|
||||
|
||||
if opt.ServiceAccountFile == "" && opt.ServiceAccountCredentials == "" && !opt.EnvAuth {
|
||||
return oauthutil.ConfigOut("teamdrive", &oauthutil.Options{
|
||||
OAuth2Config: driveConfig,
|
||||
})
|
||||
return fs.ConfigGoto("client_id")
|
||||
}
|
||||
return fs.ConfigGoto("teamdrive")
|
||||
case "client_id":
|
||||
if clientID, _ := m.Get(config.ConfigClientID); clientID != "" {
|
||||
return fs.ConfigGoto("oauth")
|
||||
}
|
||||
return oauthutil.SharedClientIDConfigConfirm("client_id_warning", "Google Drive", "https://rclone.org/drive/#making-your-own-client-id")
|
||||
case "client_id_warning":
|
||||
if configIn.Result == "true" {
|
||||
// Continue using the shared client_id
|
||||
return fs.ConfigGoto("oauth")
|
||||
}
|
||||
return fs.ConfigInput("client_id_set", config.ConfigClientID, "Google Application Client Id")
|
||||
case "client_id_set":
|
||||
m.Set(config.ConfigClientID, configIn.Result)
|
||||
return fs.ConfigInput("client_secret_set", config.ConfigClientSecret, "Google Application Client Secret")
|
||||
case "client_secret_set":
|
||||
m.Set(config.ConfigClientSecret, configIn.Result)
|
||||
return fs.ConfigGoto("oauth")
|
||||
case "oauth":
|
||||
return oauthutil.ConfigOut("teamdrive", &oauthutil.Options{
|
||||
OAuth2Config: driveConfig,
|
||||
})
|
||||
case "teamdrive":
|
||||
if opt.TeamDriveID == "" {
|
||||
return fs.ConfigConfirm("teamdrive_ok", false, "config_change_team_drive", "Configure this as a Shared Drive (Team Drive)?\n")
|
||||
}
|
||||
return fs.ConfigConfirm("teamdrive_change", false, "config_change_team_drive", fmt.Sprintf("Change current Shared Drive (Team Drive) ID %q?\n", opt.TeamDriveID))
|
||||
case "teamdrive_ok":
|
||||
if config.Result == "false" {
|
||||
if configIn.Result == "false" {
|
||||
m.Set("team_drive", "")
|
||||
return nil, nil
|
||||
}
|
||||
return fs.ConfigGoto("teamdrive_config")
|
||||
case "teamdrive_change":
|
||||
if config.Result == "false" {
|
||||
if configIn.Result == "false" {
|
||||
return nil, nil
|
||||
}
|
||||
return fs.ConfigGoto("teamdrive_config")
|
||||
@@ -279,14 +301,14 @@ func init() {
|
||||
return teamDrive.Id, teamDrive.Name
|
||||
})
|
||||
case "teamdrive_final":
|
||||
driveID := config.Result
|
||||
driveID := configIn.Result
|
||||
m.Set("team_drive", driveID)
|
||||
m.Set("root_folder_id", "")
|
||||
opt.TeamDriveID = driveID
|
||||
opt.RootFolderID = ""
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown state %q", config.State)
|
||||
return nil, fmt.Errorf("unknown state %q", configIn.State)
|
||||
},
|
||||
MetadataInfo: &fs.MetadataInfo{
|
||||
System: systemMetadataInfo,
|
||||
|
||||
@@ -94,6 +94,23 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// gphotosOAuthOptions returns a copy of oauthutil.SharedOptions with
|
||||
// the client_id help tailored for Google Photos. It copies the shared
|
||||
// options rather than mutating the global slice.
|
||||
func gphotosOAuthOptions() []fs.Option {
|
||||
opts := []fs.Option{}
|
||||
for _, opt := range oauthutil.SharedOptions {
|
||||
if opt.Name == config.ConfigClientID {
|
||||
opt.Help = "OAuth Client Id.\n\nCreating your own is now strongly recommended.\nIf you leave this blank rclone uses a shared client_id which is being retired and will stop working during 2026.\nSee https://rclone.org/googlephotos/#making-your-own-client-id for how to create your own."
|
||||
}
|
||||
if opt.Name == config.ConfigClientSecret {
|
||||
opt.Help = "OAuth Client Secret.\n\nLeave blank to use rclone's shared client_id.\nIf you created your own client_id then enter its client secret here."
|
||||
}
|
||||
opts = append(opts, opt)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fs.Register(&fs.RegInfo{
|
||||
@@ -101,7 +118,7 @@ func init() {
|
||||
Prefix: "gphotos",
|
||||
Description: "Google Photos",
|
||||
NewFs: NewFs,
|
||||
Config: func(ctx context.Context, name string, m configmap.Mapper, config fs.ConfigIn) (*fs.ConfigOut, error) {
|
||||
Config: func(ctx context.Context, name string, m configmap.Mapper, configIn fs.ConfigIn) (*fs.ConfigOut, error) {
|
||||
// Parse config into Options struct
|
||||
opt := new(Options)
|
||||
err := configstruct.Set(m, opt)
|
||||
@@ -109,7 +126,7 @@ func init() {
|
||||
return nil, fmt.Errorf("couldn't parse config into struct: %w", err)
|
||||
}
|
||||
|
||||
switch config.State {
|
||||
switch configIn.State {
|
||||
case "":
|
||||
// Fill in the scopes
|
||||
if opt.ReadOnly {
|
||||
@@ -117,6 +134,25 @@ func init() {
|
||||
} else {
|
||||
oauthConfig.Scopes = scopesReadWrite
|
||||
}
|
||||
return fs.ConfigGoto("client_id")
|
||||
case "client_id":
|
||||
if clientID, _ := m.Get(config.ConfigClientID); clientID != "" {
|
||||
return fs.ConfigGoto("oauth")
|
||||
}
|
||||
return oauthutil.SharedClientIDConfigConfirm("client_id_warning", "Google Photos", "https://rclone.org/googlephotos/#making-your-own-client-id")
|
||||
case "client_id_warning":
|
||||
if configIn.Result == "true" {
|
||||
// Continue using the shared client_id
|
||||
return fs.ConfigGoto("oauth")
|
||||
}
|
||||
return fs.ConfigInput("client_id_set", config.ConfigClientID, "OAuth Client Id")
|
||||
case "client_id_set":
|
||||
m.Set(config.ConfigClientID, configIn.Result)
|
||||
return fs.ConfigInput("client_secret_set", config.ConfigClientSecret, "OAuth Client Secret")
|
||||
case "client_secret_set":
|
||||
m.Set(config.ConfigClientSecret, configIn.Result)
|
||||
return fs.ConfigGoto("oauth")
|
||||
case "oauth":
|
||||
return oauthutil.ConfigOut("warning1", &oauthutil.Options{
|
||||
OAuth2Config: oauthConfig,
|
||||
})
|
||||
@@ -136,9 +172,9 @@ IMPORTANT: Due to Google policy changes rclone can now only download photos it u
|
||||
case "warning_done":
|
||||
return nil, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown state %q", config.State)
|
||||
return nil, fmt.Errorf("unknown state %q", configIn.State)
|
||||
},
|
||||
Options: append(append(oauthutil.SharedOptions, []fs.Option{{
|
||||
Options: append(append(gphotosOAuthOptions(), []fs.Option{{
|
||||
Name: "read_only",
|
||||
Default: false,
|
||||
Help: `Set to make the Google Photos backend read only.
|
||||
|
||||
+36
-16
@@ -40,9 +40,14 @@ XX / Google Drive
|
||||
\ "drive"
|
||||
[snip]
|
||||
Storage> drive
|
||||
Google Application Client Id - leave blank normally.
|
||||
Google Application Client Id
|
||||
Leave blank to use rclone's shared client_id, or if you are using a service account.
|
||||
The shared client_id is being retired and will stop working during 2026, so creating your own is now strongly recommended.
|
||||
See https://rclone.org/drive/#making-your-own-client-id for how to create your own.
|
||||
client_id>
|
||||
Google Application Client Secret - leave blank normally.
|
||||
Google Application Client Secret
|
||||
Leave blank to use rclone's shared client_id, or if you are using a service account.
|
||||
If you created your own client_id then enter its client secret here.
|
||||
client_secret>
|
||||
Scope that rclone should use when requesting access from drive.
|
||||
Choose a number from below, or type in your own value
|
||||
@@ -64,6 +69,17 @@ scope> 1
|
||||
Service Account Credentials JSON file path - needed only if you want use SA instead of interactive login.
|
||||
service_account_file>
|
||||
Remote config
|
||||
rclone's shared Google Drive client_id is being retired and will stop working during 2026.
|
||||
Create your own to avoid interruption: https://rclone.org/drive/#making-your-own-client-id
|
||||
|
||||
Continue using the shared client_id anyway?
|
||||
y) Yes
|
||||
n) No (default)
|
||||
y/n> n
|
||||
Google Application Client Id
|
||||
client_id> 1234567890-abcdefghijklmnop.apps.googleusercontent.com
|
||||
Google Application Client Secret
|
||||
client_secret> GOCSPX-your-client-secret
|
||||
Use web browser to automatically authenticate rclone with remote?
|
||||
* Say Y if the machine running rclone has a web browser you can use
|
||||
* Say N if running rclone on a (remote) machine without web browser access
|
||||
@@ -82,8 +98,8 @@ y/n> n
|
||||
Configuration complete.
|
||||
Options:
|
||||
type: drive
|
||||
- client_id:
|
||||
- client_secret:
|
||||
- client_id: 1234567890-abcdefghijklmnop.apps.googleusercontent.com
|
||||
- client_secret: GOCSPX-your-client-secret
|
||||
- scope: drive
|
||||
- root_folder_id:
|
||||
- service_account_file:
|
||||
@@ -615,9 +631,9 @@ Here are the Standard options specific to drive (Google Drive).
|
||||
#### --drive-client-id
|
||||
|
||||
Google Application Client Id
|
||||
Setting your own is recommended.
|
||||
Leave blank to use rclone's shared client_id, or if you are using a service account.
|
||||
The shared client_id is being retired and will stop working during 2026, so creating your own is now strongly recommended.
|
||||
See https://rclone.org/drive/#making-your-own-client-id for how to create your own.
|
||||
If you leave this blank, it will use an internal key which is low performance.
|
||||
|
||||
Properties:
|
||||
|
||||
@@ -628,9 +644,9 @@ Properties:
|
||||
|
||||
#### --drive-client-secret
|
||||
|
||||
OAuth Client Secret.
|
||||
|
||||
Leave blank normally.
|
||||
Google Application Client Secret
|
||||
Leave blank to use rclone's shared client_id, or if you are using a service account.
|
||||
If you created your own client_id then enter its client secret here.
|
||||
|
||||
Properties:
|
||||
|
||||
@@ -1945,14 +1961,18 @@ not have SHA1 or SHA256 hashes especially if they were uploaded before 2018.
|
||||
|
||||
When you use rclone with Google drive in its default configuration you
|
||||
are using rclone's client_id. This is shared between all the rclone
|
||||
users. There is a global rate limit on the number of queries per
|
||||
second that each client_id can do set by Google. rclone already has a
|
||||
high quota and I will continue to make sure it is high enough by
|
||||
contacting Google.
|
||||
users.
|
||||
|
||||
It is strongly recommended to use your own client ID as the default
|
||||
rclone ID is heavily used. If you have multiple services running, it
|
||||
is recommended to use an API key for each service. The default Google
|
||||
**This shared client_id is being retired and will stop working during
|
||||
2026.** To avoid interruption you must create and use your own
|
||||
client_id, so creating one is now required rather than merely
|
||||
recommended. New remotes created with `rclone config` will warn you if
|
||||
you leave the client_id blank.
|
||||
|
||||
Using your own client_id has other benefits too. There is a global
|
||||
rate limit on the number of queries per second that each client_id can
|
||||
do set by Google. If you have multiple services running, it is
|
||||
recommended to use an API key for each service. The default Google
|
||||
quota is 10 transactions per second so it is recommended to stay under
|
||||
that number as if you use more than that, it will cause rclone to rate
|
||||
limit and make things slower.
|
||||
|
||||
@@ -50,12 +50,15 @@ XX / Google Photos
|
||||
Storage> google photos
|
||||
** See help for google photos backend at: https://rclone.org/googlephotos/ **
|
||||
|
||||
Google Application Client Id
|
||||
Leave blank normally.
|
||||
OAuth Client Id.
|
||||
Creating your own is now strongly recommended.
|
||||
If you leave this blank rclone uses a shared client_id which is being retired and will stop working during 2026.
|
||||
See https://rclone.org/googlephotos/#making-your-own-client-id for how to create your own.
|
||||
Enter a string value. Press Enter for the default ("").
|
||||
client_id>
|
||||
Google Application Client Secret
|
||||
Leave blank normally.
|
||||
OAuth Client Secret.
|
||||
Leave blank to use rclone's shared client_id.
|
||||
If you created your own client_id then enter its client secret here.
|
||||
Enter a string value. Press Enter for the default ("").
|
||||
client_secret>
|
||||
Set to make the Google Photos backend read only.
|
||||
@@ -69,6 +72,17 @@ y) Yes
|
||||
n) No
|
||||
y/n> n
|
||||
Remote config
|
||||
rclone's shared Google Photos client_id is being retired and will stop working during 2026.
|
||||
Create your own to avoid interruption: https://rclone.org/googlephotos/#making-your-own-client-id
|
||||
|
||||
Continue using the shared client_id anyway?
|
||||
y) Yes
|
||||
n) No (default)
|
||||
y/n> n
|
||||
OAuth Client Id.
|
||||
client_id> 1234567890-abcdefghijklmnop.apps.googleusercontent.com
|
||||
OAuth Client Secret.
|
||||
client_secret> GOCSPX-your-client-secret
|
||||
Use web browser to automatically authenticate rclone with remote?
|
||||
* Say Y if the machine running rclone has a web browser you can use
|
||||
* Say N if running rclone on a (remote) machine without web browser access
|
||||
@@ -253,7 +267,9 @@ Here are the Standard options specific to google photos (Google Photos).
|
||||
|
||||
OAuth Client Id.
|
||||
|
||||
Leave blank normally.
|
||||
Creating your own is now strongly recommended.
|
||||
If you leave this blank rclone uses a shared client_id which is being retired and will stop working during 2026.
|
||||
See https://rclone.org/googlephotos/#making-your-own-client-id for how to create your own.
|
||||
|
||||
Properties:
|
||||
|
||||
@@ -266,7 +282,8 @@ Properties:
|
||||
|
||||
OAuth Client Secret.
|
||||
|
||||
Leave blank normally.
|
||||
Leave blank to use rclone's shared client_id.
|
||||
If you created your own client_id then enter its client secret here.
|
||||
|
||||
Properties:
|
||||
|
||||
@@ -653,11 +670,13 @@ The Google Photos API does not support deleting albums - see [bug #135714733](ht
|
||||
|
||||
When you use rclone with Google photos in its default configuration you
|
||||
are using rclone's client_id. This is shared between all the rclone
|
||||
users. There is a global rate limit on the number of queries per
|
||||
second that each client_id can do set by Google.
|
||||
users.
|
||||
|
||||
If there is a problem with this client_id (eg quota too low or the
|
||||
client_id stops working) then you can make your own.
|
||||
**This shared client_id is being retired and will stop working during
|
||||
2026.** To avoid interruption you must create and use your own
|
||||
client_id, so creating one is now required rather than optional. New
|
||||
remotes created with `rclone config` will warn you if you leave the
|
||||
client_id blank.
|
||||
|
||||
Please follow the steps in [the google drive docs](https://rclone.org/drive/#making-your-own-client-id)
|
||||
with the following differences:
|
||||
|
||||
@@ -502,6 +502,25 @@ func SharedClientIDWarning(name, service, helpURL string, m configmap.Mapper) {
|
||||
fs.Logf(name, "This remote uses rclone's shared %s client_id, which is being retired and will stop working during 2026. Create your own client_id to avoid interruption: %s", service, helpURL)
|
||||
}
|
||||
|
||||
// SharedClientIDConfigConfirm returns a config wizard Yes/No step
|
||||
// warning that this remote would use rclone's shared client_id (which
|
||||
// is being retired) and asking whether to continue with it anyway. It
|
||||
// should only be used when the user has left client_id blank on the
|
||||
// OAuth path.
|
||||
//
|
||||
// state is the config state to go to next, service is the human
|
||||
// readable name of the service (eg "Google Drive") and helpURL points
|
||||
// at the docs describing how to make your own client_id.
|
||||
//
|
||||
// The question defaults to No to steer the user towards making their
|
||||
// own client_id.
|
||||
func SharedClientIDConfigConfirm(state, service, helpURL string) (*fs.ConfigOut, error) {
|
||||
return fs.ConfigConfirm(state, false, "config_shared_client_id", fmt.Sprintf(`rclone's shared %s client_id is being retired and will stop working during 2026.
|
||||
Create your own to avoid interruption: %s
|
||||
|
||||
Continue using the shared client_id anyway?`, service, helpURL))
|
||||
}
|
||||
|
||||
// NewClientWithBaseClient gets a token from the config file and
|
||||
// configures a Client with it. It returns the client and a
|
||||
// TokenSource which Invalidate may need to be called on. It uses the
|
||||
|
||||
Reference in New Issue
Block a user