Integrationer

Opsætning af remote login

Oprettet af Robert Holm, Ændret den Tue, 22 Sep kl. 3:50 AM af Robert Holm




Remote login gør det muligt for et medlem at logge ind i Bookhus via jeres medlemsportal og fortsætte til booking af et bestemt emne.

Jeres medlemsportal skal kontrollere medlemmets identitet og adgang, inden den sender brugeren til Bookhus.


Flow

  1. Medlemmet vælger et emne i Bookhus eller i jeres medlemsportal.
  2. Medlemmet logger ind på jeres medlemsportal.
  3. Jeres server opretter en tidsbegrænset token og en JSON-pakke med medlemsnøglen og den ønskede returadresse.
  4. JSON-pakken krypteres, og browseren omdirigeres til Bookhus’ remote login-endpoint.
  5. Bookhus validerer tokenen, finder brugeren og logger vedkommende ind. Hvis brugeroprettelse er aktiveret, kan Bookhus også oprette en bruger, som ikke findes.
  6. Efter gennemført login sendes medlemmet til returadressen, eksempelvis /book/1234.


Opsætning i Bookhus

Hvis gæster skal sendes fra Bookhus til jeres medlemsportal, angives portalens loginadresse under bookingregler på:

https://{DOMÆNE}/admin/settings


Eksempel:

https://medlemsportal.example/login?houseId={houseid}

Bookhus erstatter {houseid} med det valgte emnes numeriske id. Jeres medlemsportal skal bevare dette id gennem loginflowet og bruge det til at danne returadressen.


Aktivér indstillingerne:


  • Gå til denne side i stedet for book
  • Dette gælder kun for gæst


Hvis Bookhus skal kunne oprette medlemmer, der endnu ikke findes, aktiveres desuden Remote login opretter brugere som ikke findes under brugerindstillinger. Denne indstilling er valgfri; uden den skal brugeren allerede findes i Bookhus.



API-nøgler

Nøglerne administreres på:

https://{DOMÆNE}/Admin/Security/Apikeys

Integrationen anvender to nøgler med forskellige formål:

BeskrivelseFormål
RemoteLoginDen hemmelige nøgle, som bruges til at generere login-tokenen.
RemoteLoginCryptoNøglen, som bruges til at kryptere JSON-pakken.


Begge nøgler skal være korrekt opsat og gyldige. Opbevar dem på jeres server. Token og krypteret loginpakke skal genereres på serveren.


Endpoint og JSON-pakke

Endpointet tilgås med et GET-kald:

https://{DOMÆNE}/account/remotelogin2?data={URL-ENCODET_KRYPTERET_PAKKE}

JSON-pakken indeholder følgende felter:

FeltBetydning
tokenEn tidsbegrænset token på seks cifre, sendt som tekst.
dataBrugerens medlemsnummer hos Bookhus, sendt som tekst.
redirectUrlDen lokale sti, som brugeren skal sendes til efter login.
firstNameValgfrit fornavn, som kan bruges ved brugeroprettelse.
lastNameValgfrit efternavn, som kan bruges ved brugeroprettelse.


Eksempel før kryptering:

{  "token": "123456",  "data": "<MEDLEMSNUMMER>",  "redirectUrl": "/book/1234",  "firstName": "Anna",  "lastName": "Jensen" }

Brug feltnavnene med præcis den viste stavemåde.

Medlemsnummer


Bookhus bruger værdien i data til at slå brugeren op via medlemsnummer.

Hvis jeres integration anvender CPR-nummer som medlemsnøgle, skal CPR-nummeret derfor være registreret som medlemsnummer i Bookhus. Det er ikke tilstrækkeligt, at det alene findes i brugerens CPR-felt.

Send værdien som tekst, så eventuelle foranstillede nuller bevares.


Returadresse efter login

Returadressen skal sendes som feltet redirectUrl inde i den krypterede JSON-pakke:

"redirectUrl": "/book/1234"

1234 er emnets numeriske id i Bookhus (House.Id).

En ekstra queryparameter som &ReturnUrl=/book/1234 bliver ikke læst af remotelogin2.

Brug en lokal sti, der begynder med /, uden protokol og domæne. Hvis returadressen mangler eller ikke accepteres som lokal, sendes brugeren som udgangspunkt til /.



Kryptering og token


JSON-pakken krypteres med den eksisterende Bookhus-protokol:

  • AES med CBC og PKCS7-padding.
  • Krypteringsnøglen anvendes som UTF-8-bytes.
  • IV består af 16 nulbytes og medsendes ikke.
  • Det krypterede resultat konverteres til Base64.
  • Base64-værdien URL-encodes, inden den indsættes i URL’en.

Tokenen genereres med HMAC-SHA1, seks cifre og tidsintervaller på 30 sekunder. Tokennøglen anvendes som rå ASCII-bytes; den skal ikke Base32- eller Base64-dekodes.

Bookhus accepterer det aktuelle tidsinterval samt ét interval før og efter. Servernes ure skal derfor være synkroniserede. Generér loginlinket umiddelbart inden omdirigering.

C#-eksempel: Kryptering


using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public static class AESCrypto
{
    public static string EncryptString(string key, string text)
    {
        using (var aes = Aes.Create())
        {
            aes.Key = Encoding.UTF8.GetBytes(key);
            aes.IV = new byte[16];
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            using (var encryptor = aes.CreateEncryptor())
            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(
                    ms, encryptor, CryptoStreamMode.Write))
                using (var sw = new StreamWriter(
                    cs, new UTF8Encoding(false)))
                {
                    sw.Write(text);
                }

                return Convert.ToBase64String(ms.ToArray());
            }
        }
    }
}

C#-eksempel: Token


using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;

public static class Twofactor
{
    private static readonly DateTime UnixEpoch =
        new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);

    public static string GetPincode(string secret)
    {
        var counter =
            (long)(DateTime.UtcNow - UnixEpoch).TotalSeconds / 30;

        var counterBytes = BitConverter.GetBytes(counter);

        if (BitConverter.IsLittleEndian)
        {
            Array.Reverse(counterBytes);
        }

        using (var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(secret)))
        {
            var hash = hmac.ComputeHash(counterBytes);
            var offset = hash[hash.Length - 1] & 0x0f;

            var binary =
                ((hash[offset] & 0x7f) << 24)
                | ((hash[offset + 1] & 0xff) << 16)
                | ((hash[offset + 2] & 0xff) << 8)
                | (hash[offset + 3] & 0xff);

            return (binary % 1000000)
                .ToString("D6", CultureInfo.InvariantCulture);
        }
    }
}

C#-eksempel: Omdirigering til booking

Eksemplet forudsætter, at medlemsportalen allerede har autentificeret medlemmet og kontrolleret adgang. Medlemsnummeret skal hentes fra den autentificerede medlemsprofil.


public ActionResult GoToBooking(int houseId)
{
    // Hentes fra serverens beskyttede konfiguration.
    var cryptoKey = "<RemoteLoginCrypto SecretKey>";
    var tokenKey = "<RemoteLogin SecretKey>";
    var domain = "cs.bookhus.dk";

    // Hentes fra det autentificerede medlem.
    var medlemsnummer = "<MEDLEMSNUMMER>";

    var package = new
    {
        token = Twofactor.GetPincode(tokenKey),
        data = medlemsnummer,
        redirectUrl = "/book/" +
            houseId.ToString(
                System.Globalization.CultureInfo.InvariantCulture)
    };

    var json = Newtonsoft.Json.JsonConvert.SerializeObject(package);
    var encryptedData = AESCrypto.EncryptString(cryptoKey, json);

    var link = string.Format(
        "https://{0}/account/remotelogin2?data={1}",
        domain,
        System.Uri.EscapeDataString(encryptedData));

    return Redirect(link);
}


Begrænsninger i den nuværende implementering

Remote login er ikke en generel synkronisering af medlemsprofiler. Eksisterende brugeres navn opdateres ikke automatisk ved hvert login.

Hvis brugeren skal gennemføre yderligere totrinsbekræftelse i Bookhus, videreføres redirectUrl ikke korrekt i den nuværende implementering. Dette kræver en rettelse i Bookhus, hvis integrationen skal understøtte dette flow.


Teksten er kontrolleret mod den lokale kildekode; drift og kodeeksempler er ikke afprøvet. Ingen filer ændret eller builds/tests kørt. TFVC-status er uændret: seks tilføjede SQL-migrationer og én ændret Web.config.

Var denne artikel nyttig?

Fantastisk!

Tak for din feedback

Beklager, at vi ikke var nyttige

Tak for din feedback

Fortæl os, hvordan vi kan forbedre denne artikel!

Vælg mindst én af grundene
Captcha-bekræftelse er påkrævet.

Feedback sendt

Vi sætter pris på din indsats og vil forsøge at rette artiklen