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:
Nick Craig-Wood
2026-07-07 12:35:37 +01:00
parent d03eb58586
commit b5a81dab76
5 changed files with 156 additions and 40 deletions
+32 -10
View File
@@ -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,
+40 -4
View File
@@ -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
View File
@@ -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.
+29 -10
View File
@@ -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:
+19
View File
@@ -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