Authentifizierung und Autorisierung

Alle Funktionen beginnen mit einer Verbindung zum secucard Backend. Die Autorisierung zu diesem erfolgt mit OAuth2, einem offenen Berechtigungsstandard. Autorisierung bedeutet grundsätzlich, dass Sie zugelassen (genehmigt) werden müssen, einen bestimmten Dienst zu nutzen. Damit wir wissen, ob Sie dazu berechtigt sind, müssen wir zunächst wissen, wer Sie sind. Dies entspricht dem Prozess der Authentifizierung.

Wenn Sie sich oder Ihre Installation authentifiziert haben, erhalten Sie ein Sicherheitstoken ( access token ), der Sie für alle Dienste autorisieren muss. Dieser access token wird mit nahezu allen API-Serveranfragen gesendet, um zu verifizieren, dass die Anfrage von einer autorisierten Quelle stammt. Diese access token sind für eine begrenzte Zeit gültig, in der Regel 20 Minuten. Es ist daher ratsam, dieses Token zu speichern und sie für alle API-Serveranfragen zu verwenden, solange der Token gültig ist. Dies vermeidet häufige und unnötige Authentifizierungen.

Nach erfolgreicher Authentifizierung erhalten Sie neben dem access token auch ein Aktualisierungs-Token ( refresh token ). Immer wenn Sie den access token verwenden, um sich gegenüber dem Backend zu authentifizieren, wird der refresh token verwendet, um im Falle eines abgelaufenen access token 's ein neuen zu erhalten. Nach der Aktualisierung erhalten Sie den neu erhaltenen access token zur weiteren Verwendung.

Unsere bereitgestellten SDK's übernehmen diese Details für Sie. Sie müssen lediglich diese Dinge zur Verfügung stellen:

  • statische Anmeldeinformationen ( Client - credentials )

  • eine UUID zur eindeutigen Identifizierung der einzelnen Shopware Plugin Installation

  • Eine Speicherschnittstelle, die vom Client verwendet wird um Informationen wie die zuvor erwähnten access token und refresh token abzulegen. Der Client braucht die volle Kontrolle über diese Schnittstelle, die so einfach sein kann wie ein einfaches Listenobjekt, welches im Speicher liegt. Der Client überprüft zunächst immer den Speicher auf das Vorhandensein eines access token . Das Löschen dieses Speichers bewirkt somit implizit, dass der Client sich erneut authentifizieren muss und einen neuen access token erhält.

Um Ihr Plugin mit dem secucard Backend zu verbinden, ist eine Zwei-Wege-Authentifizierung erforderlich. Das bedeutet, dass Sie Ihre Installationen mit Client - credentials versorgen. Diese bestehen aus einer ClientID und einem ClientSecret und werden in Abstimmung mit unserem Support vergeben. Ebenso stellen Sie uns ein Mittel zur eindeutigen Identifizierung Ihrer einzelnen Shopware Plugin Installationen mit Schlüssel- bzw. Wert-Paaren bereit. Diese Schlüssel- bzw. Wert-Paare bilden zusammen eine eindeutige Kennung oder UUID. Ein Beispiel für eine solche UUID ist: /vendor/mycompany/serial /1234 . Natürlich kann die UUID auch aus nur einem Wert zur eindeutigen Erkennung der Installation bestehen. Ein Beispiel hierfür ist: 5501510fb9e9614605abf308

Der Benutzer verbindet dann das Plugin mit unserem Server, authentifiziert es mit den von Ihnen bereitgestellten Informationen und erhält einen Bestätigungscode. Der Endbenutzer kontaktiert dann unsere Support-Hotline und übermittelt diesen Bestätigungscode ein. Das Plugin überprüft regelmäßig mit dem Server, ob der Bestätigungscode verifiziert wurde und erhält einen refresh token , sobald dies der Fall ist.

Credentials und Abstimmung der UUID

Vor Beginn Ihrer Implementierungsarbeiten benötige Sie wie oben erwähnt zunächst Ihre Credentials. Diese stellt Ihnen secucard, bestehend aus Client ID und einem Client Secret zur Verfügung. Diese Credentials sind für alle Installationen der Applikation identisch. Weiterhin muss die UUID abgestimmt und von uns im System hinterlegt werden.

Implementierungsbeispiele

Die Authentifizierung Ihrer Applikation sollte beispielsweise durch einem Button im Einstellungsemnü Ihrer Anwendung initialisierbar sein. Bei Ausführung der Funktion durch den User sollte der Authentifizierungsvorgang mit Übermittlung der Client - Credentials und der UUID wie folgt initiiert werden:

// Get the default configuration from config.properties, but you may also use your own path or stream parameter.
InputStream configFile = new FileInputStream("src/config.properties");
final SecucardConnect.Configuration config = SecucardConnect.Configuration.get(configFile);
 
// Note: You can also add your own custom properties to the configuration, retrieve them by calling
// config.property("your-prop")
 
// Create your com.secucard.connect.auth.ClientAuthDetails implementation by extending the provided abstract class
// which stores your obtained OAuth tokens to the local disk in default folder ".smartdemostore" in the current working directory.
// You may also provide your own implementation which saves to database etc. and maybe gets the credentials from
// custom properties in the configuration.
config.clientAuthDetails = new AbstractClientAuthDetails(".smartdemostore")
{
@Override
public OAuthCredentials getCredentials() {
return new DeviceCredentials(
"...", // Add your client id (current length: 32 chars)
"...", // Add your client secret (current length: 64 chars)
"/vendor/.../uuid/..." // Add your uuid like: '/vendor/.../uuid/ka01.2347'
);
}
 
@Override
public ClientCredentials getClientCredentials() {
return (ClientCredentials) this.getCredentials();
}
};
 
// Get an API client instance.
final SecucardConnect client = SecucardConnect.create(config);
 
// Set up event listener, required to handle auth events!
client.onAuthEvent(new EventListener()
{
@Override
public void onEvent(Object event)
{
if (event instanceof DeviceAuthCode) {
// Device code retrieved successfully - present this data to user.
// User must visit URL in DeviceAuthCode.verificationUrl and must enter codes.
// Client polls auth server in background meanwhile until success or timeout (config: auth.waitTimeoutSec ).
DeviceAuthCode code = (DeviceAuthCode) event;
System.out.println("Please visit: " + code.getVerificationUrl() + " and enter code: " + code.getUserCode());
}
 
if (com.secucard.connect.auth.Events.EVENT_AUTH_PENDING.equals(event)) {
// Present to the user - this event comes up periodically as long the authentication is not performed.
System.out.println("Please wait, authentication is pending.");
}
 
if (com.secucard.connect.auth.Events.EVENT_AUTH_OK.equals(event)) {
// Present to the user - user has device codes codes typed in and the auth was successful.
System.out.println("Gratulations, you are now authenticated!");
}
}
});
 
// Add a listener
client.onConnectionStateChanged(new EventListener<Events.ConnectionStateChanged>() {
@Override
public void onEvent(Events.ConnectionStateChanged event) {
System.out.println((event.connected ? "Connected to" : "Disconnected from") + " the secucard server.");
}
});
 
// Set an optional global exception handler -- all exceptions thrown by service methods end up here. If not set,
// each method throws as usual, and its up to the developer to catch accordingly. If callback are used, all
// exceptions go to the failed method.
/*
client.setServiceExceptionHandler(new ExceptionHandler() {
@Override
public void handle(Throwable exception) {
}
});
*/
 
// This will clear an existing token and will trigger an new authentication process when calling client.open()!
// authDetails.clear();
 
// Connect to the server.
// open() will trigger a new authentication if no token is present and block execution until success or error.
// Call client.cancelAuth(); to abort such a auth process (from another thread of course).
do {
try {
client.open();
break; // Success!
} catch (AuthDeniedException e) {
// Resolvable auth error like invalid credentials were given, let try again.
System.err.println("Wrong credentials " + e.getMessage());
} catch (AuthError e) {
// Unresolvable auth error like wrong client id or secret
System.err.println("Error during authentication:");
e.printStackTrace();
return;
} catch (NetworkError e) {
// either you are offline or the configuration settings are wrong
System.err.println("Can't connect, you are offline or wrong secucard host configured.");
return;
} catch (ClientError e) {
// Any other error caused by unexpected conditions (bugs, wrong config, etc.)
System.err.println("Error opening client: ");
e.printStackTrace();
return;
}
} while (true);

Können die übermittelten Client - credentials vom secucard Backend nicht verifiziert werden, so wird folgende Exception an das Plugin zurückgegeben:

Error during authentication:
com.secucard.connect.auth.exception.AuthFailedException: invalid_client, The client credentials are invalid

In diesem Fall sollte eine entsprechende Fehlermeldung, beispielsweise "Authentifizierung fehlgeschlagen: Ungültige Zugangsdaten", im UI angezeigt werden.

Kann die übermittelte UUID vom secucard Backend nicht verifiziert werden, so wird folgende Exception an das Plugin zurückgegeben:

Wrong credentials device with given uuid not configured
com.secucard.connect.auth.exception.AuthDeniedException: device with given uuid not configured

In diesem Fall sollte eine entsprechende Fehlermeldung, beispielsweise "Authentifizierung fehlgeschlagen: Unbekannte UUID", im UI angezeigt werden.

Können die übermittelten Client - credentials und die UUID vom secucard Backend verifiziert werden, so wird ein verification code an das Plugin zurückgegeben:

Please visit: http://www.secuoffice.com and enter code: zfnrfz8r

Dieser verification code sollte dann in einem neuen Layer für den User dargestellt werden, beispielsweise "Ihr Authentifizierungscode lautet "zfnrfz8r "

Anschließend wird der abgebildete verification code durch den technischen Support von Konsum über das secucard Webinterface diesen Device zuführt. Parallel dazu überprüft das Device regelmäßig, ob die Verifikation erfolgt ist. Ist dies der Fall, liefert das secucard Backend einen access token und einen refresh token . Diese beiden Token werden vom SDK standaardmäßig im defnierten Verzeichnis angelegt, in diesem Beispiel im Unterverzeichnis ".smartdemostore". Falls der access token einmal benötigt wird kann er wie folgt abgefragt werden:

System.out.println("AccessToken: " + client.getToken());
 
/*
* Sample output:
* ==============
* AccessToken: fuun4c0n49u49ir9e57t5pabc1
*/

Sobald Ihre Applikation die beiden Token erhalten und gespeichert hat, sollte das Layer mit dem verification code geschlossen oder eine positive Rückmeldung für den User erfolgen.

Es sollte jederzeit die Möglichkeit bestehen bereits erhaltene Token zu verwerfen, also aus dem Cache zu entfernen und dadurch die Authentifizierung erneut zu initiieren. Hierzu sollte eine entsprechende Möglichkeit im UI der Applikation bereitgestellt werden.