Einleitung

Alle Funktionen beginnen mit einer Verbindung zum secucard System. Die Autorisierung zu Diesem erfolgt mit dem offenen Berechtigungsstandard OAuth2. Der Begriff Autorisierung bedeutet grundsätzlich, dass Sie um einen bestimmten Dienst nutzen zu können zugelassen (genehmigt) werden müssen. Um Ihnen diese Genehmigung zu erteilen muss die Schnittstelle wissen wer die Schnittstelle aufruft. Dies entspricht dem Prozess der Authentifizierung.

Wenn Sie sich oder Ihr Gerät authentifiziert haben, erhalten Sie ein Sicherheitstoken (access token), der Sie für alle Dienste autorisieren muss. Dieser access token wird mit nahezu allen API-Serveranforderungen 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-Serveranforderungen 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 gegnüber dem Backend zu authentifizieren, wird der refresh token verwendet, um im Falles eines abgelaufenen access token's ein neuen zu erhalten. Nach der Aktualisierung verwenden Sie den neu erhaltenen access token zur weiteren Verwendung.

Unsere bereitsgestellten 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 Kassenanwendung am POS
  • Eine Speicherschnittstelle, die vom Client verwendet wird um Informationen wie den zuvor erwähnten access token abzulegen. Der Benutzer 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 Ihre Kassenanwendung mit dem secucard Backend zu verbinden, ist eine Zwei-Wege-Authentifizierung erforderlich. Das bedeutet, dass Sie Ihre Clients mit Client-credentials versorgen. Diese bestehen aus einer der Client-ID und einem Client-Secret. Sie stellen uns ebenso ein Mittel zur eindeutigen Identifizierung Ihres Geräts mit Schlüssel- bzw. Wert-Paaren bereit. Diese Schlüssel- bzw. Wert-Paare bilden zusammen eine eindeutige Gerätekennung 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 Gerätekennung bestehen. Ein Beispiel hierfür ist: 5501510fb9e9614605abf

Der Benutzer verbindet dann die Anwendung oder das Gerät mit unserem Server, authentifiziert sie mit den von Ihnen bereitgestellten Informationen und erhält einen Bestätigungscode und eine Bestätigungs-URL. Der Endbenutzer besucht dann die Bestätigungs-URL, wählt das beabsichtigte Gerät aus und gibt diesen Bestätigungscode ein. Der Client überprüft regelmäßig mit dem Server, ob der Benutzer die Entscheidung überprüft hat, und erhält ein Aktualisierungs-Token, sobald dies der Fall ist.

Implementierung in der Kasse

Wir empfehlen die Implementierung eines separaten Menüpunktes in den Einstellungen der Kassenanwendung um die Authentifizierung zu initiieren und den access token auf Wunsch zu verwerfen.

Mit Aktivierung der secucard-Funktion durch den User muss der Authentifizierungsvorgang durch die Kassenanwendung initiiert werden. Dies geschieht mit Übermittlung Client-credentials und der UUID wie folgt:

JAVA-SDK

// Get the default configuration from config.properties, but you may also use your own path or stream parameter.
// Note: You can also add your own custom properties to the configuration, retrieve them by calling
// cfg.property("your-prop")
final SecucardConnect.Configuration cfg = SecucardConnect.Configuration.get();
 
// 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.
AbstractClientAuthDetails authDetails = new AbstractClientAuthDetails(".smartdemostore") {
  @Override
  public OAuthCredentials getCredentials() {
    return new DeviceCredentials(
        "YOUR-CLIENT-ID",
        "YOUR-CLIENT-SECRET",
        "YOUR-DEVICE-ID");
  }
 
  @Override
  public ClientCredentials getClientCredentials() {
    return (ClientCredentials) this.getCredentials();
  }
};
 
cfg.clientAuthDetails = authDetails;
 
 
// Get a API client instance.
final SecucardConnect client = SecucardConnect.create(cfg);
 
// 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 ("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 ("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, 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);

.NET-SDK

public static void Run()
{
    // Load default properties
    var properties = Properties.Load("SecucardConnect.config");

    // Perpare client config. Implement your own Auth Details
    var _clientConfiguration = new ClientConfiguration(properties)
    {
        ClientAuthDetails = new DeviceAuthDetails(),
        DataStorage = new MemoryDataStorage()
    };

    // Create client and attach client event handlers
    var smartClient = SecucardConnect.Create(_clientConfiguration);
    smartClient.AuthEvent += ClientOnAuthEvent;
    smartClient.ConnectionStateChangedEvent += ClientOnConnectionStateChangedEvent;
    smartClient.Open();

    // Your logic

    smartClient.Close();
}

/// <summary>
/// Handles device authentication. Enter pin thru web interface service
/// </summary>
private static void ClientOnAuthEvent(object sender, AuthEventArgs args)
{
    if (args.Status == AuthStatusEnum.Pending)
    {
        // Device code retrieved successfully - present this data to user
        // User must visit an URL and enter some codes
        Console.WriteLine($"Please visit {args.DeviceAuthCodes.VerificationUrl} and enter code: {args.DeviceAuthCodes.UserCode}");

        // Artificial wait until the user has entered the pin
        Console.Read();
    }
    else if (args.Status == AuthStatusEnum.Ok)
    {
        // Present to the user - user has device codes typed in and the auth was successful
        Console.WriteLine("Gratulations, you are now authenticated!");
    }
}

/// <summary>
/// Handles connect and disconnect events
/// </summary>
private static void ClientOnConnectionStateChangedEvent(object sender, ConnectionStateChangedEventArgs args)
{
    Console.WriteLine("Client Connected={0}", args.Connected);
}

/// <summary>
/// You have to adjust the clientid, clientsecret and deviceid
/// </summary>
private class DeviceAuthDetails : AbstractClientAuthDetails, IClientAuthDetails
{
    public OAuthCredentials GetCredentials()
    {
        return new DeviceCredentials(
            "YOUR-CLIENT-ID",
            "YOUR-CLIENT-SECRET",
            "YOUR-DEVICE-ID");
    }

    public ClientCredentials GetClientCredentials()
    {
        return (ClientCredentials)GetCredentials();
    }
}

Können die übermittelten Client-credentials und die UUID vom secucard beackend verifiziert werden, so wird ein verification code an die Kassenanwendung zurückgegeben.

Der zurückgelieferte verification code muss nun dem User in einer Maske dargestellt werden.

Anschließend teilt der User den abgebildeten verification code telefonisch unserem Support mit, welcher diesen dem gewünschten Device zuführt. Parallel dazu überprüft der Client regelmäßig, ob die Verifikation erfolgt ist. Ist dies der Fall, liefert das secucard Backend einen acc ess token und einen refresh token.

Wurden die Token erfolgreich ausgeliefert, sollte die oben dargestellte Beispielmaske automatisiert geschlossen werden. Zudem empfehlen wir einen User-Hinweis zur erfolgten Anmeldung.

Wie bereits erwähnt, sollte ebenfalls eine Möglichkeit für den User implementiert werden, den abgelegten access token via Button zu verwerfen. Dies bewirkt dann implizit, dass sich der Client erneut authentifizieren muss und einen neuen access token erhält.