Skip to content

Commit 788179e

Browse files
committed
Add SecretSerializer for TOTP secret serialization
1 parent ffd4c58 commit 788179e

File tree

5 files changed

+54
-55
lines changed

5 files changed

+54
-55
lines changed

src/main/java/de/labystudio/spotifyapi/open/OpenSpotifyAPI.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import de.labystudio.spotifyapi.open.model.track.OpenTrack;
1010
import de.labystudio.spotifyapi.open.totp.TOTP;
1111
import de.labystudio.spotifyapi.open.totp.gson.SecretDeserializer;
12+
import de.labystudio.spotifyapi.open.totp.gson.SecretSerializer;
1213
import de.labystudio.spotifyapi.open.totp.model.Secret;
1314
import de.labystudio.spotifyapi.open.totp.provider.SecretProvider;
1415

@@ -36,6 +37,7 @@ public class OpenSpotifyAPI {
3637

3738
public static final Gson GSON = new GsonBuilder()
3839
.registerTypeAdapter(Secret.class, new SecretDeserializer())
40+
.registerTypeAdapter(Secret.class, new SecretSerializer())
3941
.create();
4042

4143
public static final String USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537." + (int) (Math.random() * 90);
@@ -103,7 +105,7 @@ private AccessTokenResponse generateAccessToken() throws IOException {
103105
}
104106

105107
long serverTime = this.requestServerTime();
106-
String totp = TOTP.generateOtp(secret.toBytes(), serverTime, 30, 6);
108+
String totp = TOTP.generateOtp(secret.getSecretAsBytes(), serverTime, 30, 6);
107109

108110
AccessTokenResponse response = this.getToken("transport", totp, secret.getVersion());
109111

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package de.labystudio.spotifyapi.open.totp.gson;
2+
3+
import com.google.gson.JsonElement;
4+
import com.google.gson.JsonObject;
5+
import com.google.gson.JsonSerializationContext;
6+
import com.google.gson.JsonSerializer;
7+
import de.labystudio.spotifyapi.open.totp.model.Secret;
8+
9+
import java.lang.reflect.Type;
10+
11+
/**
12+
* This class is used to serialize the TOTP secret to Spotify's TOTP storage format.
13+
*
14+
* @author LabyStudio
15+
*/
16+
public class SecretSerializer implements JsonSerializer<Secret> {
17+
18+
@Override
19+
public JsonElement serialize(Secret secret, Type type, JsonSerializationContext context) {
20+
JsonObject obj = new JsonObject();
21+
obj.addProperty("version", secret.getVersion());
22+
obj.addProperty("secret", secret.getSecretAsString());
23+
return obj;
24+
}
25+
}

src/main/java/de/labystudio/spotifyapi/open/totp/model/Secret.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,25 @@ private Secret(int[] secret, int version) {
1717
this.version = version;
1818
}
1919

20+
/**
21+
* Converts the secret into a string representation.
22+
*
23+
* @return A string representation of the secret, where each character corresponds to an integer in the secret array.
24+
*/
25+
public String getSecretAsString() {
26+
StringBuilder sb = new StringBuilder();
27+
for (int i : this.secret) {
28+
sb.append((char) i);
29+
}
30+
return sb.toString();
31+
}
32+
2033
/**
2134
* Converts the secret into a byte array for TOTP generation in java.
2235
*
2336
* @return A byte array representing the secret, suitable for use in TOTP generation.
2437
*/
25-
public byte[] toBytes() {
38+
public byte[] getSecretAsBytes() {
2639
// Convert secret numbers to xor results
2740
StringBuilder xorResults = new StringBuilder();
2841
for (int i = 0; i < this.secret.length; i++) {

src/main/resources/secrets.json

Lines changed: 11 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,13 @@
11
{
2-
"validUntil": "2025-07-04T13:00:00.000Z",
3-
"secrets": [
4-
{
5-
"secret": "meZcB\\tlUFV1D6W2Hy4@9+$QaH5)N8",
6-
"version": 9
7-
},
8-
{
9-
"secret": [
10-
37,
11-
84,
12-
32,
13-
76,
14-
87,
15-
90,
16-
87,
17-
47,
18-
13,
19-
75,
20-
48,
21-
54,
22-
44,
23-
28,
24-
19,
25-
21,
26-
22
27-
],
28-
"version": 8
29-
},
30-
{
31-
"secret": [
32-
59,
33-
91,
34-
66,
35-
74,
36-
30,
37-
66,
38-
74,
39-
38,
40-
46,
41-
50,
42-
72,
43-
61,
44-
44,
45-
71,
46-
86,
47-
39,
48-
89
49-
],
50-
"version": 7
51-
}
52-
]
2+
"validUntil": "2025-07-07T09:00:00.000Z",
3+
"secrets": [{
4+
"secret": "=n:b#OuEfH\\fE])e*K",
5+
"version": 10
6+
}, {
7+
"secret": "meZcB\\tlUFV1D6W2Hy4@9+$QaH5)N8",
8+
"version": 9
9+
}, {
10+
"secret": [37, 84, 32, 76, 87, 90, 87, 47, 13, 75, 48, 54, 44, 28, 19, 21, 22],
11+
"version": 8
12+
}]
5313
}

src/test/java/OpenSpotifyApiTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55
import de.labystudio.spotifyapi.open.totp.provider.SecretProvider;
66

77
public class OpenSpotifyApiTest {
8-
98
public static void main(String[] args) throws Exception {
109
SecretProvider secretProvider = new DefaultSecretProvider(
1110
// Note: You have to update the secret with the latest TOTP secret from open.spotify.com
12-
Secret.fromString("meZcB\\tlUFV1D6W2Hy4@9+$QaH5)N8", 9)
11+
Secret.fromString("=n:b#OuEfH\\fE])e*K", 10)
1312
);
1413
OpenSpotifyAPI openSpotifyAPI = new OpenSpotifyAPI(secretProvider);
1514
OpenTrack openTrack = openSpotifyAPI.requestOpenTrack("38T0tPVZHcPZyhtOcCP7pF");

0 commit comments

Comments
 (0)