Skip to content

Commit b5bd9e4

Browse files
Merge branch '252-jwt-and-multi-credentials-fixes' into 'dev'
JWT and multi-credential API fixes and improvements #252 See merge request objectbox/objectbox-java!154
2 parents f0744ce + eeb7fe9 commit b5bd9e4

File tree

8 files changed

+205
-111
lines changed

8 files changed

+205
-111
lines changed

objectbox-java/src/main/java/io/objectbox/sync/Sync.java

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2024 ObjectBox Ltd. All rights reserved.
2+
* Copyright 2019-2025 ObjectBox Ltd. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -64,13 +64,9 @@ public static SyncBuilder client(BoxStore boxStore, String url, SyncCredentials
6464
}
6565

6666
/**
67-
* Starts building a {@link SyncClient}. Once done, complete with {@link SyncBuilder#build() build()}.
67+
* Like {@link #client(BoxStore, String, SyncCredentials)}, but supports passing a set of authentication methods.
6868
*
69-
* @param boxStore The {@link BoxStore} the client should use.
70-
* @param url The URL of the Sync server on which the Sync protocol is exposed. This is typically a WebSockets URL
71-
* starting with {@code ws://} or {@code wss://} (for encrypted connections), for example
72-
* {@code ws://127.0.0.1:9999}.
73-
* @param multipleCredentials An array of {@link SyncCredentials} to be used to authenticate the user.
69+
* @param multipleCredentials An array of {@link SyncCredentials} to be used to authenticate with the server.
7470
*/
7571
public static SyncBuilder client(BoxStore boxStore, String url, SyncCredentials[] multipleCredentials) {
7672
return new SyncBuilder(boxStore, url, multipleCredentials);
@@ -87,24 +83,16 @@ public static SyncBuilder client(BoxStore boxStore, String url, SyncCredentials[
8783
* {@code ws://0.0.0.0:9999}.
8884
* @param authenticatorCredentials An authentication method available to Sync clients and peers. Additional
8985
* authenticator credentials can be supplied using the returned builder. For the embedded server, currently only
90-
* {@link SyncCredentials#sharedSecret} and {@link SyncCredentials#none} are supported.
86+
* {@link SyncCredentials#sharedSecret}, any JWT method like {@link SyncCredentials#jwtIdTokenServer()} as well as
87+
* {@link SyncCredentials#none} are supported.
9188
*/
9289
public static SyncServerBuilder server(BoxStore boxStore, String url, SyncCredentials authenticatorCredentials) {
9390
return new SyncServerBuilder(boxStore, url, authenticatorCredentials);
9491
}
9592

9693
/**
97-
* Starts building a {@link SyncServer}. Once done, complete with {@link SyncServerBuilder#build() build()}.
98-
* <p>
99-
* Note: when also using Admin, make sure it is started before the server.
100-
*
101-
* @param boxStore The {@link BoxStore} the server should use.
102-
* @param url The URL of the Sync server on which the Sync protocol is exposed. This is typically a WebSockets URL
103-
* starting with {@code ws://} or {@code wss://} (for encrypted connections), for example
104-
* {@code ws://0.0.0.0:9999}.
105-
* @param multipleAuthenticatorCredentials An authentication method available to Sync clients and peers. Additional
106-
* authenticator credentials can be supplied using the returned builder. For the embedded server, currently only
107-
* {@link SyncCredentials#sharedSecret} and {@link SyncCredentials#none} are supported.
94+
* Like {@link #server(BoxStore, String, SyncCredentials)}, but supports passing a set of authentication methods
95+
* for clients and peers.
10896
*/
10997
public static SyncServerBuilder server(BoxStore boxStore, String url, SyncCredentials[] multipleAuthenticatorCredentials) {
11098
return new SyncServerBuilder(boxStore, url, multipleAuthenticatorCredentials);
@@ -125,8 +113,8 @@ public static SyncServerBuilder server(BoxStore boxStore, String url, SyncCreden
125113
* {@code ws://0.0.0.0:9999}.
126114
* @param authenticatorCredentials An authentication method available to Sync clients and peers. The client of the
127115
* hybrid is pre-configured with them. Additional credentials can be supplied using the client and server builder of
128-
* the returned builder. For the embedded server, currently only {@link SyncCredentials#sharedSecret} and
129-
* {@link SyncCredentials#none} are supported.
116+
* the returned builder. For the embedded server, currently only {@link SyncCredentials#sharedSecret}, any JWT
117+
* method like {@link SyncCredentials#jwtIdTokenServer()} as well as {@link SyncCredentials#none} are supported.
130118
* @return An instance of {@link SyncHybridBuilder}.
131119
*/
132120
public static SyncHybridBuilder hybrid(BoxStoreBuilder storeBuilder, String url,

objectbox-java/src/main/java/io/objectbox/sync/SyncBuilder.java

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2024 ObjectBox Ltd. All rights reserved.
2+
* Copyright 2019-2025 ObjectBox Ltd. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ public final class SyncBuilder {
4242

4343
final Platform platform;
4444
final BoxStore boxStore;
45-
String url;
45+
@Nullable private String url;
4646
final List<SyncCredentials> credentials;
4747

4848
@Nullable SyncLoginListener loginListener;
@@ -94,41 +94,32 @@ private static void checkSyncFeatureAvailable() {
9494
}
9595
}
9696

97-
98-
@Internal
99-
public SyncBuilder(BoxStore boxStore, SyncCredentials credentials) {
97+
private SyncBuilder(BoxStore boxStore, @Nullable String url, @Nullable List<SyncCredentials> credentials) {
10098
checkNotNull(boxStore, "BoxStore is required.");
10199
checkNotNull(credentials, "Sync credentials are required.");
102-
checkSyncFeatureAvailable();
103-
this.platform = Platform.findPlatform();
104100
this.boxStore = boxStore;
105-
this.credentials = Collections.singletonList(credentials);
101+
this.url = url;
102+
this.credentials = credentials;
103+
checkSyncFeatureAvailable();
104+
this.platform = Platform.findPlatform(); // Requires APIs only present in Android Sync library
106105
}
107106

108107
@Internal
109-
public SyncBuilder(BoxStore boxStore, SyncCredentials[] multipleCredentials) {
110-
checkNotNull(boxStore, "BoxStore is required.");
111-
if (multipleCredentials.length == 0) {
112-
throw new IllegalArgumentException("At least one Sync credential is required.");
113-
}
114-
checkSyncFeatureAvailable();
115-
this.platform = Platform.findPlatform();
116-
this.boxStore = boxStore;
117-
this.credentials = Arrays.asList(multipleCredentials);
108+
public SyncBuilder(BoxStore boxStore, String url, @Nullable SyncCredentials credentials) {
109+
this(boxStore, url, credentials == null ? null : Collections.singletonList(credentials));
118110
}
119111

120112
@Internal
121-
public SyncBuilder(BoxStore boxStore, String url, SyncCredentials credentials) {
122-
this(boxStore, credentials);
123-
checkNotNull(url, "Sync server URL is required.");
124-
this.url = url;
113+
public SyncBuilder(BoxStore boxStore, String url, @Nullable SyncCredentials[] multipleCredentials) {
114+
this(boxStore, url, multipleCredentials == null ? null : Arrays.asList(multipleCredentials));
125115
}
126116

117+
/**
118+
* When using this constructor, make sure to set the server URL before starting.
119+
*/
127120
@Internal
128-
public SyncBuilder(BoxStore boxStore, String url, SyncCredentials[] multipleCredentials) {
129-
this(boxStore, multipleCredentials);
130-
checkNotNull(url, "Sync server URL is required.");
131-
this.url = url;
121+
public SyncBuilder(BoxStore boxStore, @Nullable SyncCredentials credentials) {
122+
this(boxStore, null, credentials == null ? null : Collections.singletonList(credentials));
132123
}
133124

134125
/**
@@ -140,6 +131,12 @@ SyncBuilder serverUrl(String url) {
140131
return this;
141132
}
142133

134+
@Internal
135+
String serverUrl() {
136+
checkNotNull(url, "Sync Server URL is null.");
137+
return url;
138+
}
139+
143140
/**
144141
* Configures a custom set of directory or file paths to search for trusted certificates in.
145142
* The first path that exists will be used.
@@ -261,7 +258,7 @@ public SyncClient buildAndStart() {
261258
return syncClient;
262259
}
263260

264-
private void checkNotNull(Object object, String message) {
261+
private void checkNotNull(@Nullable Object object, String message) {
265262
//noinspection ConstantConditions Non-null annotation does not enforce, so check for null.
266263
if (object == null) {
267264
throw new IllegalArgumentException(message);

objectbox-java/src/main/java/io/objectbox/sync/SyncClient.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2024 ObjectBox Ltd. All rights reserved.
2+
* Copyright 2019-2025 ObjectBox Ltd. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -128,14 +128,13 @@ public interface SyncClient extends Closeable {
128128
void setSyncTimeListener(@Nullable SyncTimeListener timeListener);
129129

130130
/**
131-
* Updates the login credentials. This should not be required during regular use.
131+
* Updates the credentials used to authenticate with the server. This should not be required during regular use.
132132
* The original credentials were passed when building sync client.
133133
*/
134134
void setLoginCredentials(SyncCredentials credentials);
135135

136136
/**
137-
* Updates the login credentials. This should not be required during regular use.
138-
* It allows passing login credentials that the client can use to authenticate with the server.
137+
* Like {@link #setLoginCredentials(SyncCredentials)}, but allows setting multiple credentials.
139138
*/
140139
void setLoginCredentials(SyncCredentials[] multipleCredentials);
141140

objectbox-java/src/main/java/io/objectbox/sync/SyncClientImpl.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2024 ObjectBox Ltd. All rights reserved.
2+
* Copyright 2019-2025 ObjectBox Ltd. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ public final class SyncClientImpl implements SyncClient {
6161

6262
SyncClientImpl(SyncBuilder builder) {
6363
this.boxStore = builder.boxStore;
64-
this.serverUrl = builder.url;
64+
this.serverUrl = builder.serverUrl();
6565
this.connectivityMonitor = builder.platform.getConnectivityMonitor();
6666

6767
long boxStoreHandle = builder.boxStore.getNativeStore();
@@ -189,6 +189,9 @@ public void setSyncListener(@Nullable SyncListener listener) {
189189

190190
@Override
191191
public void setLoginCredentials(SyncCredentials credentials) {
192+
if (credentials == null) {
193+
throw new IllegalArgumentException("credentials must not be null");
194+
}
192195
if (credentials instanceof SyncCredentialsToken) {
193196
SyncCredentialsToken credToken = (SyncCredentialsToken) credentials;
194197
nativeSetLoginInfo(getHandle(), credToken.getTypeId(), credToken.getTokenBytes());
@@ -204,6 +207,9 @@ public void setLoginCredentials(SyncCredentials credentials) {
204207

205208
@Override
206209
public void setLoginCredentials(SyncCredentials[] multipleCredentials) {
210+
if (multipleCredentials == null) {
211+
throw new IllegalArgumentException("credentials must not be null");
212+
}
207213
for (int i = 0; i < multipleCredentials.length; i++) {
208214
SyncCredentials credentials = multipleCredentials[i];
209215
boolean isLast = i == (multipleCredentials.length - 1);

objectbox-java/src/main/java/io/objectbox/sync/SyncCredentials.java

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2024 ObjectBox Ltd. All rights reserved.
2+
* Copyright 2019-2025 ObjectBox Ltd. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,47 +49,111 @@ public static SyncCredentials google(String idToken) {
4949
}
5050

5151
/**
52-
* ObjectBox admin users (username/password)
52+
* ObjectBox Admin user (username and password).
5353
*/
5454
public static SyncCredentials obxAdminUser(String user, String password) {
5555
return new SyncCredentialsUserPassword(CredentialsType.OBX_ADMIN_USER, user, password);
5656
}
5757

5858
/**
59-
* Generic credential type suitable for ObjectBox admin (and possibly others in the future)
59+
* Generic credentials type suitable for ObjectBox Admin (and possibly others in the future).
6060
*/
6161
public static SyncCredentials userAndPassword(String user, String password) {
6262
return new SyncCredentialsUserPassword(CredentialsType.USER_PASSWORD, user, password);
6363
}
6464

6565
/**
66-
* JSON Web Token (JWT): an ID token that typically provides identity information about the authenticated user.
66+
* Authenticate with a JSON Web Token (JWT) that is an ID token.
67+
* <p>
68+
* An ID token typically provides identity information about the authenticated user.
69+
* <p>
70+
* Use this and the other JWT methods that accept a token to configure JWT auth for a Sync client or server peer.
71+
* To configure Sync server auth options, use the server variants, like {@link #jwtIdTokenServer()}, instead.
72+
* <p>
73+
* See the <a href="https://sync.objectbox.io/sync-server-configuration/jwt-authentication">JWT authentication documentation</a>
74+
* for details.
6775
*/
6876
public static SyncCredentials jwtIdToken(String jwtIdToken) {
6977
return new SyncCredentialsToken(CredentialsType.JWT_ID_TOKEN, jwtIdToken);
7078
}
7179

7280
/**
73-
* JSON Web Token (JWT): an access token that is used to access resources.
81+
* Authenticate with a JSON Web Token (JWT) that is an access token.
82+
* <p>
83+
* An access token is used to access resources.
84+
* <p>
85+
* See {@link #jwtIdToken(String)} for some common remarks.
7486
*/
7587
public static SyncCredentials jwtAccessToken(String jwtAccessToken) {
7688
return new SyncCredentialsToken(CredentialsType.JWT_ACCESS_TOKEN, jwtAccessToken);
7789
}
7890

7991
/**
80-
* JSON Web Token (JWT): a refresh token that is used to obtain a new access token.
92+
* Authenticate with a JSON Web Token (JWT) that is a refresh token.
93+
* <p>
94+
* A refresh token is used to obtain a new access token.
95+
* <p>
96+
* See {@link #jwtIdToken(String)} for some common remarks.
8197
*/
8298
public static SyncCredentials jwtRefreshToken(String jwtRefreshToken) {
8399
return new SyncCredentialsToken(CredentialsType.JWT_REFRESH_TOKEN, jwtRefreshToken);
84100
}
85101

86102
/**
87-
* JSON Web Token (JWT): a token that is neither an ID, access, nor refresh token.
103+
* Authenticate with a JSON Web Token (JWT) that is neither an ID, access, nor refresh token.
104+
* <p>
105+
* See {@link #jwtIdToken(String)} for some common remarks.
88106
*/
89107
public static SyncCredentials jwtCustomToken(String jwtCustomToken) {
90108
return new SyncCredentialsToken(CredentialsType.JWT_CUSTOM_TOKEN, jwtCustomToken);
91109
}
92110

111+
/**
112+
* Enable authentication using a JSON Web Token (JWT) that is an ID token.
113+
* <p>
114+
* An ID token typically provides identity information about the authenticated user.
115+
* <p>
116+
* Use this and the other JWT server credentials types to configure a Sync server.
117+
* For Sync clients, use the ones that accept a token, like {@link #jwtIdToken(String)}, instead.
118+
* <p>
119+
* See the <a href="https://sync.objectbox.io/sync-server-configuration/jwt-authentication">JWT authentication documentation</a>
120+
* for details.
121+
*/
122+
public static SyncCredentials jwtIdTokenServer() {
123+
return new SyncCredentialsToken(CredentialsType.JWT_ID_TOKEN);
124+
}
125+
126+
/**
127+
* Enable authentication using a JSON Web Token (JWT) that is an access token.
128+
* <p>
129+
* An access token is used to access resources.
130+
* <p>
131+
* See {@link #jwtIdTokenServer()} for some common remarks.
132+
*/
133+
public static SyncCredentials jwtAccessTokenServer() {
134+
return new SyncCredentialsToken(CredentialsType.JWT_ACCESS_TOKEN);
135+
}
136+
137+
/**
138+
* Enable authentication using a JSON Web Token (JWT) that is a refresh token.
139+
* <p>
140+
* A refresh token is used to obtain a new access token.
141+
* <p>
142+
* See {@link #jwtIdTokenServer()} for some common remarks.
143+
*/
144+
public static SyncCredentials jwtRefreshTokenServer() {
145+
return new SyncCredentialsToken(CredentialsType.JWT_REFRESH_TOKEN);
146+
}
147+
148+
/**
149+
* Enable authentication using a JSON Web Token (JWT) that is neither an ID, access, nor refresh token.
150+
* <p>
151+
* See {@link #jwtIdTokenServer()} for some common remarks.
152+
*/
153+
public static SyncCredentials jwtCustomTokenServer() {
154+
return new SyncCredentialsToken(CredentialsType.JWT_CUSTOM_TOKEN);
155+
}
156+
93157
/**
94158
* No authentication, unsecured. Use only for development and testing purposes.
95159
*/

objectbox-java/src/main/java/io/objectbox/sync/SyncCredentialsToken.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2024 ObjectBox Ltd. All rights reserved.
2+
* Copyright 2019-2025 ObjectBox Ltd. All rights reserved.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -51,6 +51,10 @@ public final class SyncCredentialsToken extends SyncCredentials {
5151
this(type, token.getBytes(StandardCharsets.UTF_8));
5252
}
5353

54+
public boolean hasToken() {
55+
return token != null;
56+
}
57+
5458
@Nullable
5559
public byte[] getTokenBytes() {
5660
if (cleared) {

0 commit comments

Comments
 (0)