Merge pull request #13 from B00tLoad/4-save-and-retrieve-auth-tokens
4 Save and retrieve auth tokens
This commit was merged in pull request #13.
This commit is contained in:
@@ -29,6 +29,7 @@ import static de.b00tload.tools.lastfmtospotifyplaylist.util.Logger.logLn;
|
|||||||
public class LastFMToSpotify {
|
public class LastFMToSpotify {
|
||||||
|
|
||||||
public static final String LINE_SEPERATOR = System.getProperty("line.separator");
|
public static final String LINE_SEPERATOR = System.getProperty("line.separator");
|
||||||
|
public static final String USER_HOME = System.getProperty("user.home");
|
||||||
public static HashMap<String, String> configuration;
|
public static HashMap<String, String> configuration;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@@ -67,7 +68,6 @@ public class LastFMToSpotify {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logLn("Authenticating with Spotify...", 1);
|
logLn("Authenticating with Spotify...", 1);
|
||||||
SpotifyApi.Builder build = SpotifyApi.builder();
|
SpotifyApi.Builder build = SpotifyApi.builder();
|
||||||
@@ -76,23 +76,33 @@ public class LastFMToSpotify {
|
|||||||
build.setRedirectUri(URI.create("http://localhost:9876/callback/spotify/"));
|
build.setRedirectUri(URI.create("http://localhost:9876/callback/spotify/"));
|
||||||
SpotifyApi api = build.build();
|
SpotifyApi api = build.build();
|
||||||
AtomicBoolean waiting = new AtomicBoolean(true);
|
AtomicBoolean waiting = new AtomicBoolean(true);
|
||||||
try (Javalin webserver = Javalin.create().start(9876)) {
|
if (configuration.containsKey("cache.crypto") && TokenHelper.existsTokens()) {
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(webserver::stop));
|
AuthorizationCodeCredentials oldcred = TokenHelper.fetchTokens();
|
||||||
webserver.get("/callback/spotify", ctx -> {
|
AuthorizationCodeCredentials newcred = api.authorizationCodeRefresh(api.getClientId(), api.getClientSecret(), oldcred.getRefreshToken()).build().execute();
|
||||||
if(ctx.queryParamMap().containsKey("code")) {
|
TokenHelper.saveTokens(newcred);
|
||||||
AuthorizationCodeCredentials cred = api.authorizationCode(ctx.queryParam("code")).build().execute();
|
configuration.put("spotify.access", newcred.getAccessToken());
|
||||||
configuration.put("spotify.access", cred.getAccessToken());
|
} else {
|
||||||
if(configuration.containsKey("spotify.saveaccess")) TokenHelper.saveTokens(cred);
|
try (Javalin webserver = Javalin.create().start(9876)) {
|
||||||
ctx.result("success. <script>let win = window.open(null, '_self');win.close();</script>").contentType(ContentType.TEXT_HTML).status(HttpStatus.OK);
|
Runtime.getRuntime().addShutdownHook(new Thread(webserver::stop));
|
||||||
waiting.set(false);
|
// webserver.exception(Exception.class, (exception, ctx) -> {
|
||||||
} else {
|
// ctx.result(exception.getMessage());
|
||||||
logLn("Error: Spotify authorization failed."+LINE_SEPERATOR+ctx.queryParam("error"), 1);
|
// });
|
||||||
System.exit(500);
|
webserver.get("/callback/spotify", ctx -> {
|
||||||
}
|
if(ctx.queryParamMap().containsKey("code")) {
|
||||||
});
|
AuthorizationCodeCredentials cred = api.authorizationCode(ctx.queryParam("code")).build().execute();
|
||||||
logLn("Waiting for Spotify authorization.", 1);
|
configuration.put("spotify.access", cred.getAccessToken());
|
||||||
//TODO: Open auth page in Browser
|
if(configuration.containsKey("cache.crypto")) TokenHelper.saveTokens(cred);
|
||||||
while (waiting.get());
|
ctx.result("success. <script>let win = window.open(null, '_self');win.close();</script>").contentType(ContentType.TEXT_HTML).status(HttpStatus.OK);
|
||||||
|
waiting.set(false);
|
||||||
|
} else {
|
||||||
|
logLn("Error: Spotify authorization failed."+LINE_SEPERATOR+ctx.queryParam("error"), 1);
|
||||||
|
System.exit(500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
logLn("Waiting for Spotify authorization.", 1);
|
||||||
|
//TODO: Open auth page in Browser
|
||||||
|
while (waiting.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
logLn("Authenticating with LastFM...", 1);
|
logLn("Authenticating with LastFM...", 1);
|
||||||
Caller.getInstance().setApiRootUrl("https://ws.audioscrobbler.com/2.0/");
|
Caller.getInstance().setApiRootUrl("https://ws.audioscrobbler.com/2.0/");
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ public class ArgumentHandler {
|
|||||||
case NAME -> name(value);
|
case NAME -> name(value);
|
||||||
case PUBLIC -> access("public");
|
case PUBLIC -> access("public");
|
||||||
case COLLABORATIVE -> access("collaborative");
|
case COLLABORATIVE -> access("collaborative");
|
||||||
|
case SPOTIFY_CACHING -> cache(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,4 +230,12 @@ public class ArgumentHandler {
|
|||||||
|
|
||||||
configuration.put("playlist.name", name);
|
configuration.put("playlist.name", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void cache(String value){
|
||||||
|
if (value == null || value.isEmpty()) {
|
||||||
|
logLn("--spotifycache must be provided with a password. Check usage: " + Arguments.SPOTIFY_CACHING.getUsage(), 1);
|
||||||
|
System.exit(500);
|
||||||
|
}
|
||||||
|
configuration.put("cache.crypto", value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ public enum Arguments {
|
|||||||
PUBLIC("public", "[Optional] [EXCLUSIVE: public, collaborative]" + LINE_SEPERATOR
|
PUBLIC("public", "[Optional] [EXCLUSIVE: public, collaborative]" + LINE_SEPERATOR
|
||||||
+ "Makes the playlist public.", "--public", "pP"),
|
+ "Makes the playlist public.", "--public", "pP"),
|
||||||
COLLABORATIVE("collaborative", "[Optional] [EXCLUSIVE: public, collaborative]" + LINE_SEPERATOR
|
COLLABORATIVE("collaborative", "[Optional] [EXCLUSIVE: public, collaborative]" + LINE_SEPERATOR
|
||||||
+ "Makes the playlist collaborative.", "--collaborative", "pC");
|
+ "Makes the playlist collaborative.", "--collaborative", "pC"),
|
||||||
|
SPOTIFY_CACHING("spotifycache", "[Optional]" + LINE_SEPERATOR
|
||||||
|
+ "Saves and (if possible) retrieves auth tokens from a locally saved file.", "--spotifycache <password>", "sTk");
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String description;
|
private final String description;
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package de.b00tload.tools.lastfmtospotifyplaylist.util;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.crypto.*;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
import javax.crypto.spec.PBEKeySpec;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.KeySpec;
|
||||||
|
|
||||||
|
public class CryptoHelper {
|
||||||
|
|
||||||
|
public static SecretKey createKeyFromPassword(String pass){
|
||||||
|
try {
|
||||||
|
KeySpec spec = new PBEKeySpec(pass.toCharArray(), "abcdefghijklmnop".getBytes(), 65536, 256); // AES-256
|
||||||
|
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
|
||||||
|
byte[] key = new byte[0];
|
||||||
|
key = f.generateSecret(spec).getEncoded();
|
||||||
|
return new SecretKeySpec(key, "AES");
|
||||||
|
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void serializeEncrypted(Serializable obj, Path file, SecretKey key){
|
||||||
|
try {
|
||||||
|
if(file.toFile().exists()) file.toFile().delete();
|
||||||
|
if(!file.getParent().toFile().exists()) file.getParent().toFile().mkdirs();
|
||||||
|
file.toFile().createNewFile();
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
|
IvParameterSpec iv = new IvParameterSpec("abcdefghijklmnop".getBytes());
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
|
||||||
|
SealedObject sealedObject = new SealedObject( obj, cipher);
|
||||||
|
CipherOutputStream cipherOutputStream = new CipherOutputStream( new BufferedOutputStream( new FileOutputStream( file.toFile() ) ), cipher );
|
||||||
|
ObjectOutputStream outputStream = new ObjectOutputStream( cipherOutputStream );
|
||||||
|
outputStream.writeObject( sealedObject );
|
||||||
|
outputStream.close();
|
||||||
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IOException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Serializable deserializeEncrypted(Path file, SecretKey key) {
|
||||||
|
Serializable ret = null;
|
||||||
|
try {
|
||||||
|
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
|
IvParameterSpec iv = new IvParameterSpec("abcdefghijklmnop".getBytes());
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, key, iv);
|
||||||
|
CipherInputStream cipherInputStream = new CipherInputStream( new BufferedInputStream( new FileInputStream( file.toFile() ) ), cipher );
|
||||||
|
ObjectInputStream inputStream = new ObjectInputStream( cipherInputStream );
|
||||||
|
SealedObject sealedObject = (SealedObject) inputStream.readObject();
|
||||||
|
ret = (Serializable) sealedObject.getObject(cipher);
|
||||||
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IOException | IllegalBlockSizeException | ClassNotFoundException | BadPaddingException | InvalidAlgorithmParameterException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,18 +2,21 @@ package de.b00tload.tools.lastfmtospotifyplaylist.util;
|
|||||||
|
|
||||||
import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials;
|
import se.michaelthelin.spotify.model_objects.credentials.AuthorizationCodeCredentials;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static de.b00tload.tools.lastfmtospotifyplaylist.LastFMToSpotify.USER_HOME;
|
||||||
|
import static de.b00tload.tools.lastfmtospotifyplaylist.LastFMToSpotify.configuration;
|
||||||
|
|
||||||
public class TokenHelper {
|
public class TokenHelper {
|
||||||
public static void saveTokens(AuthorizationCodeCredentials cred) {
|
public static void saveTokens(AuthorizationCodeCredentials cred) {
|
||||||
//TODO: Save tokens
|
CryptoHelper.serializeEncrypted(cred, Path.of(USER_HOME, "/.lfm2s/spotify.lfm2scred"), CryptoHelper.createKeyFromPassword(configuration.get("cache.crypto")));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getAccessToken(){
|
public static AuthorizationCodeCredentials fetchTokens() {
|
||||||
//TODO: Read AccessToken
|
return (AuthorizationCodeCredentials) CryptoHelper.deserializeEncrypted(Path.of(USER_HOME, "/.lfm2s/spotify.lfm2scred"), CryptoHelper.createKeyFromPassword(configuration.get("cache.crypto")));
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getRefreshToken(){
|
public static boolean existsTokens(){
|
||||||
//TODO: Read RefreshToken
|
return Path.of(USER_HOME, "/.lfm2s/spotify.lfm2scred").toFile().exists();
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user