diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 02bb1e4..176b1c9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -27,6 +27,9 @@ jobs:
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
+ - name: Setup Gradle
+ uses: gradle/gradle-build-action@v2
+
- name: Build
run: ./gradlew build
diff --git a/README.md b/README.md
index 5c372e3..3ba929f 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,7 @@
[![Java CI](https://img.shields.io/github/workflow/status/Azuriom/AzAuth/Java%20CI?style=flat-square)](https://github.com/Azuriom/AzAuth/actions)
[![Language grade](https://img.shields.io/lgtm/grade/java/github/Azuriom/AzAuth?label=code%20quality&logo=lgtm&logoWidth=18&style=flat-square)](https://lgtm.com/projects/g/Azuriom/AzAuth/context:java)
+[![Maven Central](https://img.shields.io/maven-central/v/com.azuriom/azauth.svg?label=Maven%20Central&style=flat-square)](https://search.maven.org/search?q=g:%22com.azuriom%22%20AND%20a:%22azauth%22)
[![Chat](https://img.shields.io/discord/625774284823986183?color=5865f2&label=Discord&logo=discord&logoColor=fff&style=flat-square)](https://azuriom.com/discord)
A Java implementation of the Azuriom Auth API.
@@ -15,7 +16,7 @@ A Java implementation of the Azuriom Auth API.
com.azuriom
azauth
- 0.1.0
+ 1.0.0
```
@@ -23,8 +24,12 @@ A Java implementation of the Azuriom Auth API.
### Gradle
```groovy
+repositories {
+ mavenCentral()
+}
+
dependencies {
- implementation 'com.azuriom:azauth:0.1.0'
+ implementation 'com.azuriom:azauth:1.0.0'
}
```
diff --git a/build.gradle b/build.gradle
index e934bdd..b9df0bd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ plugins {
}
group 'com.azuriom'
-version '0.1.0'
+version '1.0.0'
ext {
isReleaseVersion = !version.endsWith('SNAPSHOT')
diff --git a/src/main/java/com/azuriom/azauth/AuthClient.java b/src/main/java/com/azuriom/azauth/AuthClient.java
new file mode 100644
index 0000000..f999dda
--- /dev/null
+++ b/src/main/java/com/azuriom/azauth/AuthClient.java
@@ -0,0 +1,324 @@
+package com.azuriom.azauth;
+
+import com.azuriom.azauth.exception.AuthException;
+import com.azuriom.azauth.gson.ColorAdapter;
+import com.azuriom.azauth.gson.InstantAdapter;
+import com.azuriom.azauth.gson.UuidAdapter;
+import com.azuriom.azauth.model.ErrorResponse;
+import com.azuriom.azauth.model.User;
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import org.jetbrains.annotations.Blocking;
+import org.jetbrains.annotations.Contract;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.awt.*;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.time.Instant;
+import java.util.Objects;
+import java.util.UUID;
+import java.util.function.Supplier;
+import java.util.logging.Logger;
+
+/**
+ * The authentication client for Azuriom.
+ */
+public class AuthClient {
+
+ private static final Logger LOGGER = Logger.getLogger(AuthClient.class.getName());
+
+ private static final Gson GSON = new GsonBuilder()
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .registerTypeAdapter(Color.class, new ColorAdapter())
+ .registerTypeAdapter(Instant.class, new InstantAdapter())
+ .registerTypeAdapter(UUID.class, new UuidAdapter())
+ .create();
+
+ private final @NotNull String url;
+
+ /**
+ * Construct a new AzAuthenticator instance.
+ *
+ * @param url the website url
+ */
+ public AuthClient(@NotNull String url) {
+ this.url = Objects.requireNonNull(url, "url");
+
+ if (!url.startsWith("https://")) {
+ LOGGER.warning("HTTP links are not secure, use HTTPS instead.");
+ }
+ }
+
+ /**
+ * Gets the website url.
+ *
+ * @return the website url
+ */
+ public @NotNull String getUrl() {
+ return this.url;
+ }
+
+ /**
+ * Try to authenticate the user on the website and get his profile.
+ *
+ * @param email the user email
+ * @param password the user password
+ * @return the user profile
+ * @throws AuthException if an error occurs (IO exception, invalid credentials, etc)
+ */
+ @Blocking
+ public @NotNull AuthResult<@NotNull User> login(@NotNull String email,
+ @NotNull String password) throws AuthException {
+ return this.login(email, password, User.class);
+ }
+
+ /**
+ * Try to authenticate the user on the website with a 2FA code, and get his profile.
+ *
+ * @param email the user email
+ * @param password the user password
+ * @param code2fa the 2FA code of the user
+ * @return the user profile
+ * @throws AuthException if an error occurs (IO exception, invalid credentials, etc)
+ */
+ @Blocking
+ public @NotNull AuthResult<@NotNull User> login(@NotNull String email,
+ @NotNull String password,
+ @Nullable String code2fa) throws AuthException {
+ return this.login(email, password, code2fa, User.class);
+ }
+
+ /**
+ * Try to authenticate the user on the website and get his profile with a given response type.
+ *
+ * @param email the user email
+ * @param password the user password
+ * @param responseType the class of the response
+ * @param the type of the response
+ * @return the user profile
+ * @throws AuthException if an error occurs (IO exception, invalid credentials, etc)
+ */
+ @Blocking
+ public @NotNull AuthResult<@NotNull T> login(@NotNull String email,
+ @NotNull String password,
+ @NotNull Class responseType) throws AuthException {
+ return login(email, password, (String) null, responseType);
+ }
+
+ /**
+ * Try to authenticate the user on the website and get his profile with a given response type.
+ * If the user has 2FA enabled, the {@code codeSupplier} will be called.
+ *
+ * @param email the user email
+ * @param password the user password
+ * @param codeSupplier the supplier called to get the 2FA code
+ * @return the user profile
+ * @throws AuthException if an error occurs (IO exception, invalid credentials, etc)
+ */
+ @Blocking
+ public @NotNull User login(@NotNull String email,
+ @NotNull String password,
+ @NotNull Supplier codeSupplier) throws AuthException {
+ return login(email, password, codeSupplier, User.class);
+ }
+
+ /**
+ * Try to authenticate the user on the website and get his profile with a given response type.
+ * If the user has 2FA enabled, the {@code codeSupplier} will be called.
+ *
+ * @param email the user email
+ * @param password the user password
+ * @param codeSupplier the supplier called to get the 2FA code
+ * @param responseType the class of the response
+ * @param the type of the response
+ * @return the user profile
+ * @throws AuthException if an error occurs (IO exception, invalid credentials, etc)
+ */
+ @Blocking
+ public @NotNull T login(@NotNull String email,
+ @NotNull String password,
+ @NotNull Supplier codeSupplier,
+ @NotNull Class responseType) throws AuthException {
+ AuthResult result = login(email, password, responseType);
+
+ if (result.isSuccess()) {
+ return result.getSuccessResult();
+ }
+
+ if (!result.isPending() || !result.asPending().require2fa()) {
+ throw new AuthException("Unknown login result: " + result);
+ }
+
+ String code = codeSupplier.get();
+
+ if (code == null) {
+ throw new AuthException("No 2FA code provided.");
+ }
+
+ result = login(email, password, code, responseType);
+
+ if (!result.isSuccess()) {
+ throw new AuthException("Unknown login result: " + result);
+ }
+
+ return result.getSuccessResult();
+ }
+
+ /**
+ * Try to authenticate the user on the website and get his profile with a 2FA code and a given response type.
+ *
+ * @param email the user email
+ * @param password the user password
+ * @param code2fa the 2FA code of the user
+ * @param responseType the class of the response
+ * @param the type of the response
+ * @return the user profile
+ * @throws AuthException if an error occurs (IO exception, invalid credentials, etc)
+ */
+ @Blocking
+ public @NotNull AuthResult<@NotNull T> login(@NotNull String email,
+ @NotNull String password,
+ @Nullable String code2fa,
+ @NotNull Class responseType) throws AuthException {
+ JsonObject body = new JsonObject();
+ body.addProperty("email", email);
+ body.addProperty("password", password);
+ body.addProperty("code", code2fa);
+
+ return this.post("authenticate", body, responseType);
+ }
+
+ /**
+ * Verify an access token and get the associated profile.
+ *
+ * @param accessToken the user access token
+ * @return the user profile
+ * @throws AuthException if an error occurs (IO exception, invalid credentials, etc)
+ */
+ @Blocking
+ public @NotNull User verify(@NotNull String accessToken) throws AuthException {
+ return this.verify(accessToken, User.class);
+ }
+
+ /**
+ * Verify an access token and get the associated profile with a given response type.
+ *
+ * @param accessToken the user access token
+ * @param responseType the class of the response
+ * @param the type of the response
+ * @return the user profile
+ * @throws AuthException if an error occurs (IO exception, invalid credentials, etc)
+ */
+ @Blocking
+ public @NotNull T verify(@NotNull String accessToken, @NotNull Class responseType)
+ throws AuthException {
+ JsonObject body = new JsonObject();
+ body.addProperty("access_token", accessToken);
+
+ AuthResult result = this.post("verify", body, responseType);
+
+ if (!result.isSuccess()) {
+ throw new AuthException("Unexpected verification result: " + result);
+ }
+
+ return result.asSuccess().getResult();
+ }
+
+ /**
+ * Invalidate the given access token.
+ * To get a new valid access token you need to use {@link #login(String, String)} again.
+ *
+ * @param accessToken the user access token
+ * @throws AuthException if an error occurs (IO exception, invalid credentials, etc)
+ */
+ @Blocking
+ public void logout(@NotNull String accessToken) throws AuthException {
+ JsonObject body = new JsonObject();
+ body.addProperty("access_token", accessToken);
+
+ this.post("logout", body, null);
+ }
+
+ @Blocking
+ @Contract("_, _, null -> null; _, _, !null -> !null")
+ private AuthResult post(@NotNull String endPoint, @NotNull JsonObject body,
+ @Nullable Class responseType) throws AuthException {
+ try {
+ return this.doPost(endPoint, body, responseType);
+ } catch (IOException e) {
+ throw new AuthException(e);
+ }
+ }
+
+ @Blocking
+ @Contract("_, _, null -> null; _, _, !null -> !null")
+ private AuthResult doPost(@NotNull String endPoint, @NotNull JsonObject body, @Nullable Class responseType)
+ throws AuthException, IOException {
+ try {
+ URL apiUrl = new URL(this.url + "/api/auth/" + endPoint);
+ HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ connection.addRequestProperty("User-Agent", "AzAuth authenticator v1");
+ connection.addRequestProperty("Content-Type", "application/json; charset=utf-8");
+
+ try (OutputStream out = connection.getOutputStream()) {
+ out.write(body.toString().getBytes(StandardCharsets.UTF_8));
+ }
+
+ int status = connection.getResponseCode();
+
+ if (status >= 400 && status < 500) {
+ return this.handleClientError(connection);
+ }
+
+ if (responseType == null) {
+ return null;
+ }
+
+ return handleResponse(connection, responseType);
+ } catch (IOException e) {
+ throw new AuthException(e);
+ }
+ }
+
+ private AuthResult handleResponse(HttpURLConnection connection, Class type) throws AuthException, IOException {
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
+ T response = GSON.fromJson(reader, type);
+
+ if (response == null) {
+ throw new AuthException("Empty JSON response from API");
+ }
+
+ return new AuthResult.Success<>(response);
+ }
+ }
+
+ private AuthResult handleClientError(HttpURLConnection connection)
+ throws AuthException, IOException {
+ int status = connection.getResponseCode();
+
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
+ ErrorResponse response = GSON.fromJson(reader, ErrorResponse.class);
+
+ if (response.getStatus().equals("pending")
+ && Objects.equals(response.getReason(), "2fa")) {
+ return new AuthResult.Pending<>(AuthResult.Pending.Reason.REQUIRE_2FA);
+ }
+
+ throw new AuthException(response.getMessage());
+ } catch (JsonParseException e) {
+ throw new AuthException("Invalid JSON response from API (http " + status + ")");
+ }
+ }
+}
diff --git a/src/main/java/com/azuriom/azauth/AuthResponse.java b/src/main/java/com/azuriom/azauth/AuthResponse.java
deleted file mode 100644
index 1538997..0000000
--- a/src/main/java/com/azuriom/azauth/AuthResponse.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.azuriom.azauth;
-
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Objects;
-
-public class AuthResponse {
-
- private final String status;
- private final String message;
-
- public AuthResponse(@NotNull String status, @NotNull String message) {
- this.status = Objects.requireNonNull(status, "status");
- this.message = Objects.requireNonNull(message, "message");
- }
-
- /**
- * Get the response status.
- *
- * @return the response status
- */
- public @NotNull String getStatus() {
- return this.status;
- }
-
- /**
- * Get the response message.
- *
- * @return the response message
- */
- public @NotNull String getMessage() {
- return this.message;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- AuthResponse that = (AuthResponse) o;
- return this.status.equals(that.status) && this.message.equals(that.message);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(this.status, this.message);
- }
-
- @Override
- public String toString() {
- return "AuthStatus{status='" + this.status + "', message='" + this.message + "'}";
- }
-}
diff --git a/src/main/java/com/azuriom/azauth/AuthResult.java b/src/main/java/com/azuriom/azauth/AuthResult.java
new file mode 100644
index 0000000..604fdbf
--- /dev/null
+++ b/src/main/java/com/azuriom/azauth/AuthResult.java
@@ -0,0 +1,110 @@
+package com.azuriom.azauth;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Objects;
+
+public interface AuthResult {
+
+ default boolean isSuccess(){
+ return this instanceof Success;
+ }
+
+ default Success asSuccess() {
+ return (Success) this;
+ }
+
+ default T getSuccessResult() {
+ return asSuccess().getResult();
+ }
+
+ default boolean isPending() {
+ return this instanceof Pending;
+ }
+
+ default Pending asPending() {
+ return (Pending) this;
+ }
+
+ default Pending.Reason getPendingReason() {
+ return asPending().getReason();
+ }
+
+ class Success implements AuthResult {
+ private final T result;
+
+ public Success(@NotNull T result) {
+ this.result = Objects.requireNonNull(result);
+ }
+
+ public @NotNull T getResult() {
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Success> success = (Success>) o;
+ return this.result.equals(success.result);
+ }
+
+ @Override
+ public int hashCode() {
+ return result.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Success{result=" + result + '}';
+ }
+ }
+
+ class Pending implements AuthResult {
+ private final Reason reason;
+
+ public Pending(Reason reason) {
+ this.reason = reason;
+ }
+
+ public Reason getReason() {
+ return reason;
+ }
+
+ public boolean require2fa() {
+ return this.reason == Reason.REQUIRE_2FA;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Pending> pending = (Pending>) o;
+ return this.reason == pending.reason;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.reason.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Pending{reason=" + reason + '}';
+ }
+
+ public enum Reason {
+ REQUIRE_2FA
+ }
+ }
+}
diff --git a/src/main/java/com/azuriom/azauth/AuthenticationException.java b/src/main/java/com/azuriom/azauth/AuthenticationException.java
deleted file mode 100644
index e1635ab..0000000
--- a/src/main/java/com/azuriom/azauth/AuthenticationException.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.azuriom.azauth;
-
-public class AuthenticationException extends Exception {
-
- public AuthenticationException(String message) {
- super(message);
- }
-
- public AuthenticationException(String message, Throwable cause) {
- super(message, cause);
- }
-
- public AuthenticationException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/src/main/java/com/azuriom/azauth/AzAuthenticator.java b/src/main/java/com/azuriom/azauth/AzAuthenticator.java
deleted file mode 100644
index 17e6f09..0000000
--- a/src/main/java/com/azuriom/azauth/AzAuthenticator.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package com.azuriom.azauth;
-
-import com.azuriom.azauth.gson.ColorAdapter;
-import com.azuriom.azauth.gson.InstantAdapter;
-import com.azuriom.azauth.gson.UuidAdapter;
-import com.azuriom.azauth.model.User;
-import com.google.gson.FieldNamingPolicy;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonObject;
-import org.jetbrains.annotations.Blocking;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.awt.*;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.time.Instant;
-import java.util.Objects;
-import java.util.UUID;
-import java.util.logging.Logger;
-
-public class AzAuthenticator {
-
- private static final Logger LOGGER = Logger.getLogger(AzAuthenticator.class.getName());
-
- private static final Gson GSON = new GsonBuilder()
- .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
- .registerTypeAdapter(Color.class, new ColorAdapter())
- .registerTypeAdapter(Instant.class, new InstantAdapter())
- .registerTypeAdapter(UUID.class, new UuidAdapter())
- .create();
-
- private final String url;
-
- /**
- * Construct a new AzAuthenticator instance.
- *
- * @param url the website url
- */
- public AzAuthenticator(@NotNull String url) {
- this.url = Objects.requireNonNull(url, "url");
-
- if (!url.startsWith("https://")) {
- LOGGER.warning("HTTP links are not secure, use HTTPS instead.");
- }
- }
-
- /**
- * Gets the website url.
- *
- * @return the website url
- */
- public @NotNull String getUrl() {
- return this.url;
- }
-
- /**
- * Try to authenticate the user on the website and get his profile.
- *
- * @param email the user email
- * @param password the user password
- * @return the user profile
- * @throws AuthenticationException if credentials are not valid
- * @throws IOException if an IO exception occurs
- */
- @Blocking
- public @NotNull User authenticate(@NotNull String email, @NotNull String password)
- throws AuthenticationException, IOException {
- return this.authenticate(email, password, User.class);
- }
-
- /**
- * Try to authenticate the user on the website and get his profile with a given response type.
- *
- * @param email the user email
- * @param password the user password
- * @param responseType the class of the response
- * @param the type of the response
- * @return the user profile
- * @throws AuthenticationException if credentials are not valid
- * @throws IOException if an IO exception occurs
- */
- @Blocking
- public @NotNull T authenticate(@NotNull String email, @NotNull String password, @NotNull Class responseType)
- throws AuthenticationException, IOException {
- JsonObject body = new JsonObject();
- body.addProperty("email", email);
- body.addProperty("password", password);
-
- return this.post("authenticate", body, responseType);
- }
-
- /**
- * Verify an access token and get the associated profile.
- *
- * @param accessToken the user access token
- * @return the user profile
- * @throws AuthenticationException if credentials are not valid
- * @throws IOException if an IO exception occurs
- */
- @Blocking
- public @NotNull User verify(@NotNull String accessToken) throws AuthenticationException, IOException {
- return this.verify(accessToken, User.class);
- }
-
- /**
- * Verify an access token and get the associated profile with a given response type.
- *
- * @param accessToken the user access token
- * @param responseType the class of the response
- * @param the type of the response
- * @return the user profile
- * @throws AuthenticationException if credentials are not valid
- * @throws IOException if an IO exception occurs
- */
- @Blocking
- public @NotNull T verify(@NotNull String accessToken, @NotNull Class responseType)
- throws AuthenticationException, IOException {
- JsonObject body = new JsonObject();
- body.addProperty("access_token", accessToken);
-
- return this.post("verify", body, responseType);
- }
-
- /**
- * Invalidate the given access token.
- * To get a new valid access token you need to use {@link #authenticate(String, String)} again.
- *
- * @param accessToken the user access token
- * @throws AuthenticationException if credentials are not valid
- * @throws IOException if an IO exception occurs
- */
- @Blocking
- public void logout(@NotNull String accessToken) throws AuthenticationException, IOException {
- JsonObject body = new JsonObject();
- body.addProperty("access_token", accessToken);
-
- this.post("logout", body, null);
- }
-
- @Blocking
- @Contract("_, _, null -> null; _, _, !null -> !null")
- private T post(@NotNull String endPoint, @NotNull JsonObject body, @Nullable Class responseType)
- throws AuthenticationException, IOException {
- URL apiUrl = new URL(this.url + "/api/auth/" + endPoint);
- HttpURLConnection connection = (HttpURLConnection) apiUrl.openConnection();
- connection.setRequestMethod("POST");
- connection.setDoOutput(true);
- connection.addRequestProperty("User-Agent", "AzAuth authenticator v1");
- connection.addRequestProperty("Content-Type", "application/json; charset=utf-8");
-
- try (OutputStream out = connection.getOutputStream()) {
- out.write(body.toString().getBytes(StandardCharsets.UTF_8));
- }
-
- if (connection.getResponseCode() == 422) {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getErrorStream()))) {
- AuthResponse status = GSON.fromJson(reader, AuthResponse.class);
-
- throw new AuthenticationException(status.getMessage());
- }
- }
-
- if (responseType == null) {
- return null;
- }
-
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
- T response = GSON.fromJson(reader, responseType);
-
- if (response == null) {
- throw new IllegalStateException("Empty JSON response");
- }
-
- return response;
- }
- }
-}
diff --git a/src/main/java/com/azuriom/azauth/exception/AuthException.java b/src/main/java/com/azuriom/azauth/exception/AuthException.java
new file mode 100644
index 0000000..e6539a2
--- /dev/null
+++ b/src/main/java/com/azuriom/azauth/exception/AuthException.java
@@ -0,0 +1,16 @@
+package com.azuriom.azauth.exception;
+
+public class AuthException extends Exception {
+
+ public AuthException(String message) {
+ super(message);
+ }
+
+ public AuthException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public AuthException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/azuriom/azauth/model/ErrorResponse.java b/src/main/java/com/azuriom/azauth/model/ErrorResponse.java
new file mode 100644
index 0000000..f0ea004
--- /dev/null
+++ b/src/main/java/com/azuriom/azauth/model/ErrorResponse.java
@@ -0,0 +1,84 @@
+package com.azuriom.azauth.model;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Objects;
+
+/**
+ * The content of the response when an error occurs.
+ */
+public class ErrorResponse {
+
+ private final String status;
+ private final String message;
+ private final String reason;
+
+ /**
+ * Create a new error response.
+ *
+ * @param status the response status
+ * @param message the response message
+ * @param reason the response reason
+ */
+ public ErrorResponse(@NotNull String status, @NotNull String message, @Nullable String reason) {
+ this.status = Objects.requireNonNull(status, "status");
+ this.message = Objects.requireNonNull(message, "message");
+ this.reason = reason;
+ }
+
+ /**
+ * Get the response status.
+ *
+ * @return the response status
+ */
+ public @NotNull String getStatus() {
+ return this.status;
+ }
+
+ /**
+ * Get the response message.
+ *
+ * @return the response message
+ */
+ public @NotNull String getMessage() {
+ return this.message;
+ }
+
+ /**
+ * Get the response error/pending reason, or null.
+ *
+ * @return the reason
+ */
+ public @Nullable String getReason() {
+ return reason;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ErrorResponse that = (ErrorResponse) o;
+ return this.status.equals(that.status)
+ && this.message.equals(that.message)
+ && Objects.equals(this.reason, that.reason);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.status, this.message, this.reason);
+ }
+
+ @Override
+ public String toString() {
+ return "AuthStatus{status='" + this.status
+ + "', message='" + this.message
+ + "', reason='" + this.reason
+ + "'}";
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/azuriom/azauth/AuthenticatorTest.java b/src/test/java/com/azuriom/azauth/ClientTest.java
similarity index 57%
rename from src/test/java/com/azuriom/azauth/AuthenticatorTest.java
rename to src/test/java/com/azuriom/azauth/ClientTest.java
index c45c639..8525ba7 100644
--- a/src/test/java/com/azuriom/azauth/AuthenticatorTest.java
+++ b/src/test/java/com/azuriom/azauth/ClientTest.java
@@ -1,5 +1,6 @@
package com.azuriom.azauth;
+import com.azuriom.azauth.exception.AuthException;
import com.azuriom.azauth.model.User;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
@@ -12,39 +13,38 @@
@EnabledIfEnvironmentVariable(named = "AUTH_TEST_EMAIL", matches = ".+"),
@EnabledIfEnvironmentVariable(named = "AUTH_TEST_PASSWORD", matches = ".+"),
})
-class AuthenticatorTest {
+class ClientTest {
- private final AzAuthenticator authenticator = new AzAuthenticator(System.getenv("AUTH_TEST_URL"));
+ private final AuthClient client = new AuthClient(System.getenv("AUTH_TEST_URL"));
private final String testEmail = System.getenv("AUTH_TEST_EMAIL");
private final String testPassword = System.getenv("AUTH_TEST_PASSWORD");
@Test
void testAuthenticateAndLogout() {
- User user = assertDoesNotThrow(() -> authenticator.authenticate(this.testEmail, this.testPassword));
+ AuthResult result = assertDoesNotThrow(() -> client.login(this.testEmail, this.testPassword));
+ assertTrue(result.isSuccess());
+ User user = result.getSuccessResult();
assertEquals(this.testEmail, user.getEmail());
-
assertNotNull(user.getAccessToken());
assertNotNull(user.getCreatedAt());
assertNotNull(user.getRole());
assertNotNull(user.getRole().getColor());
- assertNotNull(user.getUuid());
-
- User verifiedUser = assertDoesNotThrow(() -> authenticator.verify(user.getAccessToken()));
-
- assertEquals(user, verifiedUser);
- assertDoesNotThrow(() -> authenticator.logout(user.getAccessToken()));
+ User verifyResult = assertDoesNotThrow(() -> client.verify(user.getAccessToken()));
- assertThrows(AuthenticationException.class, () -> authenticator.verify(user.getAccessToken()));
+ assertEquals(user, verifyResult);
+ assertDoesNotThrow(() -> client.logout(user.getAccessToken()));
+ assertThrows(AuthException.class, () -> client.verify(user.getAccessToken()));
}
@Test
void testAuthenticateWithType() {
- CustomUser user = assertDoesNotThrow(() ->
- authenticator.authenticate(this.testEmail, this.testPassword, CustomUser.class));
+ AuthResult result = assertDoesNotThrow(() ->
+ client.login(this.testEmail, this.testPassword, CustomUser.class));
- assertEquals(this.testEmail, user.email);
+ assertTrue(result.isSuccess());
+ assertEquals(this.testEmail, result.getSuccessResult().email);
}
static class CustomUser {
diff --git a/src/test/java/com/azuriom/azauth/gson/UuidAdapterTest.java b/src/test/java/com/azuriom/azauth/gson/UuidAdapterTest.java
index e2d1d0c..faf8adb 100644
--- a/src/test/java/com/azuriom/azauth/gson/UuidAdapterTest.java
+++ b/src/test/java/com/azuriom/azauth/gson/UuidAdapterTest.java
@@ -6,7 +6,7 @@
import java.util.UUID;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.*;
class UuidAdapterTest {