Feathers: Unterstützung für Aktualisierungstoken hinzufügen

Erstellt am 22. Dez. 2015  ·  64Kommentare  ·  Quelle: feathersjs/feathers

Wir erlauben derzeit das Abrufen eines neuen Tokens, indem ein gültiges Authentifizierungstoken an <loginEndpoint>/refresh gesendet wird. Aktualisierungstoken haben einen etwas anderen Workflow, wie hier erläutert:
https://auth0.com/learn/refresh-tokens

Authentication Feature

Hilfreichster Kommentar

Diese Funktion ist ein MUSS, wenn es um React Native Apps geht. Der Benutzer loggt sich zu Beginn ein und wenn er die App nach mehreren Wochen öffnet, erwartet er, noch eingeloggt zu sein.

Alle 64 Kommentare

:+1: @corymsmith und ich haben darüber gesprochen. In der Hoffnung, etwas davon in den "Ferien" über die Ziellinie zu bringen.

Wir haben Unterstützung dafür in master, aber auch Unterstützung dafür im decoupling Zweig. Um einen Token zu aktualisieren, haben Sie 2 Möglichkeiten:

  1. Sie können sich entweder mit E-Mail/Passwort, Twitter usw. erneut authentifizieren.
  2. Sie können ein gültiges Token an GET /auth/token/refresh

Wir haben zwar einen Token-Erneuerungsprozess, aber keine vollständige Unterstützung für die Aktualisierung von Token, wie im oben geposteten Auth0-Link beschrieben. Ein tatsächliches Aktualisierungstoken funktioniert ähnlich wie ein GitHub-Authentifizierungscode/-passwort, kann jedoch nur verwendet werden, um ein neues JWT-Token abzurufen. Auch wenn Ihr JWT-Token abläuft, können Sie sich mit einem Aktualisierungstoken erneut anmelden. Sie werden mit intakter Benutzer-ID in der Datenbank gespeichert und können jederzeit widerrufen werden. Zumindest entnehme ich das dem Auth0-Artikel.

Ah du hast recht @marshallswain. Ich schätze, ich hätte auf den Link klicken sollen :wink:

Ich denke, für den ersten Schnitt lassen wir das dann vom 1.0-Meilenstein weg. Es ist einfach genug für die Leute, sich einfach erneut zu authentifizieren.

Ich stimme irgendwie dafür, dass wir das zu einer feathers-authentication 2.0 Sache machen.

Große Köpfe.

Ich bin immer noch ziemlich verwirrt, da nicht klar ist, wie genau der Authentifizierungsworkflow funktioniert.

Was ich gerade tue, ist.
1.) Kunde sendet Benutzername und Passwort

 curl -X POST https://xxx/auth/local   -H "Content-Type: application/json"   -d '{ "email":"xxx", "password":"yyy"}'

Dies gibt das JWT-Token zurück.

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjYyODUsImV4cCI6MTQ3MDQxMjY4NSwiaXNzIjoiZmVhdGhlcnMifQ.OVvQbnxfoDGxPFm3Y6tBhRae2Qa6_mDq-PVIo8RcC8Y"}

2.) Dann füge ich dieses Token in den Autorizatin-HTTP-Header ein, um auf die API zuzugreifen.

curl -X GET https://xxx/users  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY'

Die Sache, die ich nicht verstehe, ist als nächstes, wie dieses Token tatsächlich aktualisiert wird.
Ich habe versucht, dieses Token an xxx/auth/token/refresh . zu senden
Was ich bekommen habe, ist nur ein weiteres sehr langes Token. Ich habe dann versucht, sowohl das alte als auch dieses neue Token zu verwenden, um auf die API zuzugreifen. beides funktioniert... (sollte das alte nicht deaktiviert werden?)

curl -X GET https://xxx/auth/token/refresh  -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY'
{"query":{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY"},"provider":"rest","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJxdWVyeSI6eyJ0b2tlbiI6ImV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUpmYVdRaU9pSTFOemhoTmpVeU4yUmtNVFppTWpJd01EUmhZMlpqTm1FaUxDSnBZWFFpT2pFME56QXpNalUxTnpZc0ltVjRjQ0k2TVRRM01EUXhNVGszTml3aWFYTnpJam9pWm1WaGRHaGxjbk1pZlEuX0NIZHgzUnBFdUkxODl0OTBtWHEtSU1QWFJOdW9WaDduQndZMU9ON3hDWSJ9LCJwcm92aWRlciI6InJlc3QiLCJ0b2tlbiI6ImV5SjBlWEFpT2lKS1YxUWlMQ0poYkdjaU9pSklVekkxTmlKOS5leUpmYVdRaU9pSTFOemhoTmpVeU4yUmtNVFppTWpJd01EUmhZMlpqTm1FaUxDSnBZWFFpT2pFME56QXpNalUxTnpZc0ltVjRjQ0k2TVRRM01EUXhNVGszTml3aWFYTnpJam9pWm1WaGRHaGxjbk1pZlEuX0NIZHgzUnBFdUkxODl0OTBtWHEtSU1QWFJOdW9WaDduQndZMU9ON3hDWSIsImRhdGEiOnsiX2lkIjoiNTc4YTY1MjdkZDE2YjIyMDA0YWNmYzZhIiwiaWF0IjoxNDcwMzI1NTc2LCJleHAiOjE0NzA0MTE5NzYsImlzcyI6ImZlYXRoZXJzIiwidG9rZW4iOiJleUowZVhBaU9pSktWMVFpTENKaGJHY2lPaUpJVXpJMU5pSjkuZXlKZmFXUWlPaUkxTnpoaE5qVXlOMlJrTVRaaU1qSXdNRFJoWTJaak5tRWlMQ0pwWVhRaU9qRTBOekF6TWpVMU56WXNJbVY0Y0NJNk1UUTNNRFF4TVRrM05pd2lhWE56SWpvaVptVmhkR2hsY25NaWZRLl9DSGR4M1JwRXVJMTg5dDkwbVhxLUlNUFhSTnVvVmg3bkJ3WTFPTjd4Q1kifSwiaWF0IjoxNDcwMzI2NDQyLCJleHAiOjE0NzA0MTI4NDIsImlzcyI6ImZlYXRoZXJzIn0.TqUv3051TTGbX4cPfkN-6pOOB5SN9nH-E7TU1HHSsb8","data":{"_id":"578a6527dd16b22004acfc6a","iat":1470325576,"exp":1470411976,"iss":"feathers","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NzhhNjUyN2RkMTZiMjIwMDRhY2ZjNmEiLCJpYXQiOjE0NzAzMjU1NzYsImV4cCI6MTQ3MDQxMTk3NiwiaXNzIjoiZmVhdGhlcnMifQ._CHdx3RpEuI189t90mXq-IMPXRNuoVh7nBwY1ON7xCY"}}

Noch seltsamer ist, dass ich versucht habe, dieses neue Token zu verwenden und erneut an /auth/token/refresh zu senden.
Ich habe ein noch längeres Token als dieses.

Ich bin mir nicht sicher, was ich hier falsch gemacht oder falsch verstanden habe. Bitte vorschlagen.

@parnurzeal wir haben noch nicht wirklich Unterstützung für Aktualisierungs-Token. Aus diesem Grund ist dies eine vorgeschlagene Funktion.

Um ein neues Token zu erhalten, führen Sie einen POST an /auth/token mit Ihrem vorhandenen gültigen JWT durch oder melden Sie sich mit einem anderen Authentifizierungsmechanismus an. Anscheinend machst du alles richtig.

Richtig, aber schauen Sie sich bitte genau an, wie ich es gemacht habe und welches Ergebnis ich erhalten habe.

Nehmen wir ein einfaches Beispiel.
Wenn ich ein neues Token mit abcdefghijklmno anfordere (nur zufälliges Unsinn-Token).
Die Rückantwort ist nur eine längere Version des vorherigen Tokens -> abcdefghijklmnopqrstuvwxyz
Wenn ich es erneut mit abcdefghijklmnopqrstuvwxyz versuche, bekomme ich eine längere Version davon ->
abcdefghijklmnopqrstuvwxyz1234567890 und die Schleife geht weiter (wenn Sie mehr anfordern, erhalten Sie eine längere, längere Version der vorherigen).

Außerdem sind alle drei oben genannten Token alle gleichzeitig verwendbar.
Sollte das vorherige Token nicht ablaufen, nachdem wir ein neues Token angefordert haben?

@parnurzeal was ich sage ist, tun Sie nicht, was Sie getan haben, weil diese Funktion nicht wirklich implementiert ist. Basierend auf der Implementierung (bisher) liegt die Tatsache, dass der Token jedes Mal, wenn Sie /auth/token/refresh treffen, weiter wächst, daran, dass wir die Daten nur zurück in den Token schieben. So soll es nicht funktionieren und wir hatten keine Zeit, es fertig zu stellen und warum dies nicht dokumentiert ist. Sie dürfen es nicht verwenden.

Sollte das vorherige Token nicht ablaufen, nachdem wir ein neues Token angefordert haben?

Dies ist die Natur von JWT. Sie laufen selbstständig von ihrer TTL ab. Wenn Sie verhindern möchten, dass alte Token verwendet werden, die noch nicht abgelaufen sind, müssen Sie eine Blacklist führen. Derzeit ist dies Ihnen überlassen und wir haben ein offenes Problem (#133) dazu, werden aber wahrscheinlich nicht so schnell (wenn überhaupt) dazu kommen.

Hallo, ich habe nach /auth/refresh/token geschaut und bin mit so etwas herausgekommen:

...
function pick (o, ...props) {
  return Object.assign({}, ...props.map(prop => ({[prop]: o[prop]})));
}

// Provider specific config
const defaults = {
  payload: ['id', 'role'],
  passwordField: 'password',
  issuer: 'feathers',
  algorithm: 'HS256',
  expiresIn: '1d', // 1 day
};
...
// GET /auth/token/refresh
  get (id, params) {
    if (id !== 'refresh') {
      return Promise.reject(new errors.NotFound());
    }

    const options = this.options;

    // Add payload fields
    const data = pick(params.payload, options.payload);

    return new Promise(resolve => {
      jwt.sign(data, config.get('auth').token.secret, options, token => {
        return resolve({token: token});
      });
    });

  }

Ist die Umsetzung zu naiv? Wenn nicht, könnte ich versuchen, zu polieren, ein paar Tests hinzuzufügen und eine PR zu erstellen.

@aboutlo danke für die Mühe! Es ist am besten, zu warten, bis v0.8 herauskommt (es ist seit einiger Zeit in Alpha), da eine Reihe von Änderungen stattgefunden haben und diese Route diese Woche möglicherweise eingestellt wird.
Ich schneide heute eine Beta-Version und schließe derzeit den Migrationsleitfaden ab. Es wird also nicht mehr lange dauern und v0.8 behebt viele der aktuellen Probleme mit auth.

Wir haben uns viele Gedanken über die Aktualisierung von Token gemacht. Sobald 0.8 (diese Woche) veröffentlicht wird, würde ich mich gerne mit diesem Thema befassen, um es zu diskutieren. Ich werde wahrscheinlich später in dieser Woche unsere vorläufigen Gedanken veröffentlichen.

fair genug @ekryski , ich werde auf die 0.8 warten :)

Diese Funktion ist ein MUSS, wenn es um React Native Apps geht. Der Benutzer loggt sich zu Beginn ein und wenn er die App nach mehreren Wochen öffnet, erwartet er, noch eingeloggt zu sein.

@deiucanta Die gute Nachricht ist, dass wir diese Funktion bei der Entwicklung von behalten haben. Ich glaube nicht, dass es lange dauern wird, bis wir es eingerichtet und dokumentiert haben.

Das sind gute Neuigkeiten! freue mich darauf

@marshallswain Ich freue mich auf ein Update zu dieser Funktion. Bitte lassen Sie mich wissen, wann wir damit rechnen können. Oder ist es schon freigegeben? Danke im Voraus.

@deiucanta Bis zur Veröffentlichung dieser Funktion können Sie in der Zwischenzeit langlebigere Token verwenden. Nach der Freigabe können Sie Ihr Authentifizierungsgeheimnis auf einen neuen Wert ändern, um alle vorhandenen Sitzungen zu löschen und alle Ihre Benutzer auf die kürzeren, erneuernden Sitzungen zu bringen.

@atulrpandey es ist nicht offiziell veröffentlicht, aber es ist auch nicht schwer zu implementieren. Sie fügen einfach einen Hook hinzu, um ein neues Aktualisierungstoken zu generieren und dieses auf dem Benutzerobjekt in der Datenbank zu speichern, und wenn es aufgebraucht oder abgelaufen ist, entfernen Sie es vom Benutzer.

@petermikitsh Eine andere Sache, die Sie tun können (wenn Sie auf dem Handy sind), besteht darin, clientId und clientSecret sicher auf dem Client zu speichern und wenn das JWT-AccessToken abläuft, müssen Sie sich einfach mit diesen erneut authentifizieren.

@ekryski können Sie bitte ein Beispiel für eine dieser Strategien
Oder wird es lange dauern, bis die offizielle Unterstützung veröffentlicht wird? Dies wird wirklich bei der mobilen Authentifizierung helfen!

Zum Thema Refresh-Token:

Aktualisierungstoken enthalten die Informationen, die zum Abrufen eines neuen Zugriffstokens erforderlich sind. Mit anderen Worten, wenn ein Zugriffstoken für den Zugriff auf eine bestimmte Ressource erforderlich ist, kann ein Client ein Aktualisierungstoken verwenden, um ein neues Zugriffstoken zu erhalten, das vom Authentifizierungsserver ausgegeben wird. Häufige Anwendungsfälle sind der Erhalt neuer Zugriffstoken nach Ablauf der alten oder der erstmalige Zugriff auf eine neue Ressource. Aktualisierungstoken können auch ablaufen, sind aber ziemlich langlebig. Aktualisierungstoken unterliegen normalerweise strengen Speicheranforderungen, um sicherzustellen, dass sie nicht durchgesickert sind. Sie können auch vom Autorisierungsserver auf https://auth0.com/blog/refresh-tokens-what-are-they-and-when-to-use-them/

Also bin ich früher selbst zu React Native gekommen, als ich dachte. Ich denke darüber nach, dies möglicherweise beizutragen, aber ich möchte sicher sein, dass ich die Mechanik vollständig verstehe, um sicherzustellen, dass es sich um die richtige Implementierung handelt. Da Aktualisierungstoken nicht zustandslos sind, gibt es einige Einschränkungen bei der Verwendung (z. B. müssen Entwickler einen Speicheradapter bereitstellen).

Können Sie ein gültiges JWT verwenden, um ein Aktualisierungstoken zu erhalten? Oder werden in der Authentifizierungsantwort automatisch Aktualisierungstoken zurückgegeben (z. B. zusätzlich zu accessToken )? Es sieht so aus, als ob Auth0 sie in Authentifizierungsantworten (sowohl accessToken als auch refreshToken ) in ihrem Beispiel unter https://auth0.com/learn/refresh-tokens/ einschließt

@petermikitsh Ich denke nicht, dass Sie ein gültiges JWT verwenden können sollten, um ein Aktualisierungstoken zu erhalten. Wenn Sie dies tun, kann jeder ein JWT erhalten und Zugriff auf ein Konto behalten.

Aktualisierungstoken werden normalerweise mit Anmelde-/Anmeldeantworten zurückgegeben, und der Client kann dann für die bestimmte Sitzung nicht wirklich wieder darauf zugreifen, es sei denn, er meldet sich erneut an, wodurch er eine neue Sitzung und ein neues Aktualisierungstoken erhält.

Aktualisierungstoken müssen nicht wirklich ablaufen, können aber aufgerufen werden, wenn sie in der Datenbank gespeichert werden. Auf diese Weise kann der Benutzer auch sehen, wie viele aktive Sitzungen er hat. Sie könnten mehr Informationen speichern, wenn Sie Aktualisierungstokens ausgeben (wie Betriebssystem, IP, Gerätename usw., um sie identifizierbar zu machen - wie Facebook, GitHub).

Zumindest mache ich das so.

Es sollte in Ordnung sein, ein gültiges JWT zu verwenden, um ein Aktualisierungstoken zu erhalten, solange Sie die Autorisierung sowohl beim Ausgeben des Aktualisierungstokens als auch beim Versuch, das Aktualisierungstoken zu verwenden, überprüfen.

@marshallswain

Wir erlauben derzeit das Abrufen eines neuen Tokens, indem ein gültiges Authentifizierungstoken an <loginEndpoint>/refresh gesendet wird. Aktualisierungstoken haben einen etwas anderen Workflow ...

Also sollte es renew nicht refresh heißen, um die Verwirrung zu vermeiden <loginEndpoint>/renew

Wie @abhishekbhardwaj sagte

accessToken, sollte nicht durch ein accessToken aktualisierbar sein, sondern nur durch ein refresehToken oder Benutzername/Passwort, ein refreshToken sollte nur durch eine Benutzer/Passwort-Authentifizierung aktualisierbar sein, oder ein anderes Geheimnis, auf das der Browser nicht zugreifen kann, wie ein 2-Faktor auth...

Derzeit ist es möglich, Ihr accessToken mit einem accessToken zu aktualisieren, wie hier erwähnt:

https://github.com/feathersjs/authentication-jwt/issues/61

Ein anderer Ansatz besteht darin, das Aktualisierungstoken in der Nutzlast von accessToken zu speichern, dann überprüft die aktuelle Aktualisierungs-API, ob das Aktualisierungstoken nicht widerrufen wurde (über Datenbank oder Redis-Aufruf). Auf diese Weise könnte das Aktualisierungstoken eine einfache Auto-Inkrement-ID sein. Auch die Aktualisierung der API sollte den Ablauf nicht mehr überprüfen. Da das Aktualisierungstoken (einfache Ganzzahl) mit dem Zugriffstoken signiert ist, ist es sicher.

Auf diese Weise sollten die Änderungen an der aktuellen Codebasis minimal sein: Stellen Sie für die Aktualisierungs-API eine Möglichkeit bereit, damit der Benutzer einen Hook bereitstellen kann, um zu überprüfen, ob ein Zugriffstoken nicht widerrufen wurde (durch Überprüfen des Aktualisierungstokens in seiner Nutzlast), wenn dieser Hook bereitgestellt wird don' t validieren die Ablaufzeit mehr.

Könnten Sie diesen Ansatz @arash16 etwas näher

Wenn Sie das Refresh-Token in der accessTokens-Nutzlast speichern und die Refresh-API den Ablauf nicht überprüft, haben Sie nicht einfach jedes AccessToken auf "nicht ablaufend" gesetzt

Weil jedes accessToken verwendet werden könnte, um ein neues Zugriffstoken zu erhalten, oder?

Verpasse ich etwas?

@BigAB
Ich meinte, überprüfen Sie nicht den Ablauf nur für refresh vom Benutzer manuell widerrufen werden .

Der Entwickler sollte eine DB-Tabelle/Redis haben, um alle Refresh-IDs zu speichern. Wenn ein Benutzer einige (oder alle) andere Sitzungen widerrufen oder sich abmelden muss, können wir ihm eine Liste aller Aktualisierungs-IDs (plus einige andere zusätzliche Informationen wie Browser- oder Erstellungsdatum usw.) -aus) sie selektiv. Sobald das eigentliche Token mit diesen Refresh-IDs abgelaufen ist, weigert sich die Refresh-API, ein neues zu geben.

Die Auffrischungs-ID im Token wird die meiste Zeit nicht verwendet und die Autorisierung ist zustandslos, bis das Token abläuft. Danach haben wir möglicherweise einen einzigen Aufruf an db, um die Auffrischungs-ID zu validieren und ein neues Zugriffstoken zurückzugeben.

Die Ablaufzeit des Zugriffstokens könnte kurz sein (weniger als 10 Minuten), der Benutzer kann die Seite schließen und weggehen, später, wenn er die Seite öffnet, ist der Zugriffstoken bereits abgelaufen und er wird abgemeldet. Die Refresh-ID im Token hat jedoch eine viel längere Lebensdauer, die von der Datenbank verwaltet wird (z. B. 7 oder 30 Tage) und auch manuell widerrufbar ist.

Aus Sicherheitsgründen sollte das so verwendete Zugriffstoken wie ein alter Sitzungsschlüssel behandelt werden, mit dem zusätzlichen Vorteil, dass wir nicht jedes Mal die Datenbank aufrufen müssen, um ihn zu validieren (nur nach Ablauf).

@arash16 , ich mag Ihre Idee, das Aktualisierungstoken im Zugriffs-JWT zu speichern. Gibt es ein Beispiel, wie dieses Aktualisierungstoken auf der Serverseite abgerufen wird?

Mein aktuelles Problem: Wenn das Zugriffstoken abgelaufen ist, ist die Nutzlast im Hook-Kontext von Federn nicht verfügbar. Ich denke, eine Möglichkeit wäre, die Dienstprogrammfunktion verifyJWT() des Pakets @feathersjs/authentication , zB ganz am Anfang von app.service('authentication').hooks({ before: { create: ... } }) ?

Gibt es derzeit eine achtsame Möglichkeit, Aktualisierungsmarker in Federn zu verwenden? Gibt es Pläne, Unterstützung für sie hinzuzufügen?

Hallo alle ,

Sieht so aus, als ob es immer noch nicht verfügbar ist (oder vermisse ich etwas). Könnten Sie uns bitte mitteilen, wann es verfügbar sein wird? Ich sehe, dass ein Repository für Passport-Refresh-Token verfügbar ist. Hat das jemand probiert?
https://github.com/fiznool/passport-oauth2-refresh

Hallo @daffl ,
Kann mir jemand helfen zu verstehen, wie Token für die Google-Strategie aktualisiert werden können? Weil ich kein Passwort für das Google-Login-Szenario habe?

Vielen Dank

Ein anderer Ansatz besteht darin, das Aktualisierungstoken in der Nutzlast von accessToken zu speichern

Also kann jeder, der auch nur einen Ihrer sogar abgelaufenen Zugriffstoken hat, problemlos eine endlose Anzahl neuer Zugriffstoken generieren oder übersehe ich etwas?

Aktualisierungstoken sollten sicher auf dem Client gespeichert werden und niemand außer diesem Client sollte Zugriff darauf haben!

@deiucanta Die gute Nachricht ist, dass wir diese Funktion bei der Entwicklung von behalten haben. Ich glaube nicht, dass es lange dauern wird, bis wir es eingerichtet und dokumentiert haben.

Dies wurde 2016 veröffentlicht. Leute, habt ihr noch Pläne, dieses unverzichtbare Feature zu unterstützen?

Nein. Sie können mit einem abgelaufenen Token nichts machen. Außerdem sind Refresh-Token im v4-Prerelease viel einfacher möglich. Ein Kochbucheintrag für die Vorgehensweise wird Teil der endgültigen Veröffentlichung sein.

Außerdem sind Refresh-Token im v4-Prerelease viel einfacher möglich.

Gibt es ein ungefähres Erscheinungsdatum?

Ein Kochbucheintrag für die Vorgehensweise wird Teil der endgültigen Veröffentlichung sein.

Könnten Sie bis zur Veröffentlichung dieses Handbuchs in wenigen Worten erklären, wie dies in der Vorabversion von v4 möglich ist?

@daffl ping

Gibt es dafür schon eine offizielle Möglichkeit? v4 ist da und ich sehe nichts in den docs

@daffl könnten Sie erläutern, wie dies mit 4.0 und ohne Hacks für den Authentifizierungsdienst erreichbar ist?

@MichaelErmer als Workaround können Sie eine lokale oder eine beliebige benutzerdefinierte Strategie verwenden, um jwt zu erneuern, nicht ideal, funktioniert aber gut für die interne Kommunikation, sagen wir zwischen Worker und API.

function initAuth() {
  return async (ctx) => {
    if (ctx.path !== 'authentication') {
      const [authenticated, accessToken] = await Promise.all([
        ctx.app.get('authentication'),
        ctx.app.authentication.getAccessToken(),
      ]);

      if (!accessToken || !authenticated) {
        const result = await ctx.app.authenticate(apiLocalCreds);
        ctx.params = {
          ...ctx.params,
          ...result,
          headers: { ...(ctx.params.headers || {}), Authorization: result.accessToken },
        };
      } else {
        const { exp } = decode(accessToken);
        const expired = Date.now() / 1000 > exp - 60 * 60;
        if (expired) {
          const result = await ctx.app.authenticate(apiLocalCreds);
          ctx.params = {
            ...ctx.params,
            ...result,
            headers: { ...(ctx.params.headers || {}), Authorization: result.accessToken },
          };
        }
      }
    }
    return ctx;
  };
}

client
  .configure(rest(apiHost).superagent(superagent))
  .configure(auth(authConfig))
  .hooks({ before: [initAuth()] });

Derzeit verwende ich diesen after Hook in v4 authentication , um mein accessToken nach 20 Tagen zu aktualisieren...

````javascript
const {DateTime} = require('luxon')
const renewAfter = {Tage: 20}

module.exports = () => {
asynchronen Kontext zurückgeben => {
wenn (
context.method === 'erstellen' &&
context.type === 'nach' &&
context.path === 'Authentifizierung' &&
context.data && context.data.strategy === 'jwt' &&
Kontext.Ergebnis &&
context.result.accessToken) {
// prüfen, ob Token erneuert werden muss
const payload = wait context.app.service('authentication').verifyAccessToken(context.result.accessToken)
const ausgegebenAt = DateTime.fromMillis(payload.iat * 1000)
const renewAfter = issueAt.plus(renewAfter)
const jetzt = DateTime.local()
if (jetzt > erneuernNach) {
context.result.accessToken = wait context.app.service('authentication').createAccessToken({sub: payload.sub})
}
}
Kontext zurückgeben
}
}
````

Es ist wichtig, diesen Hook in after und als letzten Hook zu haben, damit alle Überprüfungen usw. bestanden wurden

Gibt es Pläne, Aktualisierungstoken in Federn zu integrieren?

Ich unterstütze diese Frage eine Nachricht zuvor.

Ich frage mich auch über den Aktualisierungstoken-Workflow. Ist die von @m0dch3n entworfene Lösung eine bewährte Vorgehensweise? Sollen wir es anders implementieren?

Der gesamte refreshToken-Workflow schützt meiner Meinung nach nur wenig gegen Man-in-the-Middle-Angriffe, so dass, wenn der Mittelsmann das accessToken stiehlt, er es zumindest nicht aktualisieren kann und unendlichen Zugriff auf die Ressourcen hat.

Es schützt nicht vor XSS, da der Angreifer in diesem Fall alles stehlen kann, was auf der Client-Seite gespeichert ist. Also auch der refreshToken...

Das Problem ist jetzt, dass wenn Sie die Ablaufzeit Ihres accessToken zu klein machen (zB 5 Minuten), Sie es auch öfter aktualisieren müssen. Der Mann in der Mitte braucht nur 5 Minuten lang auf die Client-Anfragen zu hören, um dann das refreshToken abzufangen... Wenn Sie den Ablauf verlängern, hat er nur mit dem accessToken länger Zugriff...

Ehrlich gesagt, wenn mir ein Kunde sagt, dass sein Zugang gestohlen wurde, muss ich accessToken UND refreshToken sowieso auf die schwarze Liste setzen, um sicher zu gehen. Ich bin also gezwungen, bei jeder Anfrage sowieso eine DB-Anfrage zu stellen.

In meinem Fall, wenn mir ein solcher Fall bekannt ist, setze ich alle accessTokens der letzten 40 Tage auf die Blacklist, da meine accessTokens eine Gültigkeit von 40 Tagen haben...

Die Verwendung von HTTPS-Anfragen macht Man-in-the-Middle-Angriffe wirklich schwierig. Verwenden Sie keine HTTPS-Anfragen?

Natürlich verwende ich https, aber es gibt 3 Möglichkeiten den accessToken zu stehlen. Der erste ist auf der Client-Seite (XSS dh), der zweite auf dem Transport (man in the middle) und der dritte auf der Server-Seite.

Beim Kunden und beim Transport bin ich nur zur Hälfte für die Sicherheit verantwortlich und die andere Hälfte ist der Kunde, der nicht vollständig unter meiner Kontrolle steht. Aber ich kann dem Kunden helfen, Sicherheitsrisiken zu vermeiden, indem ich XSS unmöglich mache und den Transport mit https absichere...

Das Ziel eines refreshToken ist es, den Ablauf eines accessToken zu verkürzen Anfrage keinen längeren oder unendlich gültigen Token zu übertragen

Die einzige Sicherheit, die es bringt, ist also, dass Sie ab 100 Anfragen nicht alle 100 beim Transport verwundbar machen, sondern nur 1 Anfrage

Im Grunde kann also ein Man-in-the-Middle-Angriff nicht durch ein refeshToken und natürlich nicht durch ein XSS geschützt werden... Es kann nur reduziert werden, wie oft Sie dieses RefreshToken übertragen... Die Kosten für die Übertragung sind geringer Allerdings muss das accessToken länger gültig sein...

Ich kopiere/übertrage einfach meine Kommentare aus dem Slack-Kanal:

Ich denke, das Aktualisierungstoken ist ein Muss, um eine Funktion zu unterstützen, und es geht nicht darum, vorhandenes Zugriffstoken automatisch zu erneuern. Das Zugriffstoken ist zustandslos und wird nicht serverseitig gespeichert. Der Nachteil ist, dass es für immer gültig ist! Je länger das Zugriffstoken ist, desto mehr Risiken werden auferlegt. Wenn der Zugriffstoken jedoch zu kurz ist, müssen sich Ihre Benutzer häufig anmelden, was sich stark auf die Benutzerfreundlichkeit auswirkt.

Hier kommt das Aktualisierungstoken ins Spiel, das Token, das zum Aktualisieren des Zugriffstokens verwendet wird, und es ist ein langlebiges Token. Wenn das Zugriffstoken abgelaufen ist, kann der Client das Aktualisierungstoken verwenden, um ein neues Zugriffstoken abzurufen, und dies ist der einzige Zweck des Aktualisierungstokens.

Das Aktualisierungstoken kann widerrufen werden, wenn das Benutzerkonto kompromittiert wurde. Und das ist der große Unterschied zwischen Zugriffstoken und Aktualisierungstoken. Um ein ausgestelltes Aktualisierungstoken zu widerrufen, muss der Server alle ausgestellten Aktualisierungstoken speichern. Mit anderen Worten, das Aktualisierungstoken ist zustandsbehaftet. Der Server muss wissen, welcher gültig ist, welcher ungültig ist.

Um das Aktualisierungstoken ordnungsgemäß zu implementieren, benötigen wir eine Art Tokenspeicher, um das Aktualisierungstoken beizubehalten. Außerdem müssen wir mindestens drei Flüsse implementieren:

Token-Validierung aktualisieren
Zugriffstoken mit gültigem Aktualisierungstoken aktualisieren
Aktualisierungstoken des kompromittierten Benutzers widerrufen

Es gibt auch andere Verwaltungsfunktionen, wie z. B. Token-Nutzungsstatistiken.

Oben ist mein aktuelles Verständnis bezüglich der Implementierung des Aktualisierungstokens. Es ist nicht einfach, aber es ist definitiv notwendig, ein sichereres System aufzubauen.

Es stellt sich heraus, dass Feathers bereits alle Funktionen/Module integriert hat, die für die ordnungsgemäße Implementierung von Refresh-Token erforderlich sind:

  1. Refresh-Token Store: kann problemlos von Feathers Service unterstützt werden.
  2. Ausgabe und Validierung des Aktualisierungstokens: Kann nur die vorhandene JWT-Unterstützung mit dem integrierten AuthenticationService wiederverwenden.

Basierend auf der Arbeit von TheSinding (https://github.com/TheSinding/authentication-refresh-token) habe ich meine eigene Version von Refresh-Token mit einem benutzerdefinierten Dienst und drei Hooks implementiert (https://github.com/ jackywxd/feathers-refresh-token), die grundlegende Refresh-Token-Funktionen ermöglicht:

  1. Aktualisierungstoken nach erfolgreicher Benutzerauthentifizierung ausgeben;
  2. Aktualisieren Sie das Zugriffstoken mit einem gültigen JWT-Aktualisierungstoken;
  3. Benutzer abmelden durch Löschen des Refresh-Tokens

Während die vorhandene Codebasis in Feathres vollständig genutzt wird, ist der tatsächliche Codierungsaufwand minimal und lässt sich gut in die aktuelle Feathers-Architektur integrieren. Es beweist, dass die aktuelle Feathers-Architektur sehr erweiterbar ist.

Eine vollständige Funktion von Refresh-Token erfordert jedoch auch Unterstützung auf der Client-Seite, wie z.

Nach der Überprüfung des Quellcodes von Feather-Authentifizierung und Authentifizierungsclient glaube ich, dass das Refresh-Token in den vorhandenen Code basierend auf Features angezapft werden könnte, um das Aktivieren der Refresh-Token-Unterstützung so einfach wie das Aktivieren der Authentifizierung zu ermöglichen.

Ich habe meine Hooks-Versions-Refresh-Token-Codebasis bereits in @feathersjs/authentication portiert. Als nächstes würde ich versuchen, Änderungen am Authentifizierungsclient vorzunehmen, um clientseitige Funktionen zu aktivieren. Mein ultimatives Ziel ist es, die Unterstützung für Aktualisierungstoken sowohl auf der Server- als auch auf der Clientseite zu ermöglichen.

Meine Frage/Mein Anliegen ist, wie das Aktualisierungstoken im Client gespeichert werden würde?

Siehe https://auth0.com/blog/securing-single-page-applications-with-refresh-token-rotation/

Leider sind langlebige RTs nicht für SPAs geeignet, da es keinen persistenten Speichermechanismus in einem Browser gibt, der den Zugriff nur durch die beabsichtigte Anwendung sicherstellen kann. Da es Schwachstellen gibt, die ausgenutzt werden können, um diese hochwertigen Artefakte zu erhalten und böswilligen Akteuren Zugriff auf geschützte Ressourcen zu gewähren, wird von der Verwendung von Aktualisierungstoken in SPAs dringend abgeraten.

Siehe https://afteracademy.com/blog/implement-json-web-token-jwt-authentication-using-access-token-and-refresh-token

Was ist also der beste Ort, um die Token sicher aufzubewahren? Sie können im Internet mehr darüber lesen, wenn Sie leidenschaftlich daran interessiert sind, eine vollständig sichere Speicherung zu erreichen. Einige der Lösungen sind ideal, aber nicht sehr praktisch. Praktisch würde ich es in den Cookies mit httpOnly und Secure Flags speichern. Es ist nicht 100-prozentig sicher, aber es erledigt die Arbeit.

Siehe auch diese lange Diskussion über Cookies - https://github.com/feathersjs-ecosystem/authentication/issues/132

@bwgjoseph Ich würde vorschlagen, eine reguläre Express-Sitzung zu verwenden und alle Token dort statt auf der Clientseite zu speichern. Das ist, was ich tue und funktioniert perfekt mit allen Arten von Apps, einschließlich SPA

@sarkistlt Sie meinen, alle Client-JWT-Token serverseitig zu speichern? Gibt es dazu Referenz-/Artikelmaterial? Ich bin mir nicht ganz sicher, wie der Prozess aussehen würde. Was sendet der Client also, wenn er Daten anfordert (CRUD)?

@bwgjoseph wie immer, Cookie, fügen Sie einfach Middlewere hinzu, bevor Sie Ihre Dienste registrieren:

app.use('* | [or specific rout]', session(sess), (req, res, next) => {
      req.feathers.session = req.session || {};
      next();
    });

Wenn der Kunde authentifiziert ist, speichern Sie dann auf Ihrem Server, zum Beispiel für den Kunden-Login-Service, einfach Token in der Sitzung wie ctx.params.session.token = token , wobei Token Ihr JWT-Zugriffs- oder Aktualisierungstoken ist, abhängig von Ihrer Anwendungslogik.
Und bei jeder neuen Anfrage vom Client überprüfen Sie, ob ein Token in der Sitzung vorhanden ist und verwenden es zur Authentifizierung. Dies ist ein viel sichererer Ansatz, da keines der Token auf der Clientseite offengelegt wird.

Ich füge nur hinzu, dass dies am besten für Client-(Browser)-Server-Anwendungen funktioniert. Bei der internen Kommunikation zwischen Servern oder Workern/Servern benötigen Sie keine Sitzung.

Dies wurde schon _viel_ diskutiert (ich habe auch einen Eintrag in die FAQ hinzugefügt) und es ist nicht unbedingt sicherer, einen Token in einer Sitzung zu speichern. Wenn jemand Zugriff auf Ihre Seite erhält, um Skripte ausführen zu können, hat er auch die Sitzung gekapert und kann trotzdem authentifizierte Anfragen stellen.

Daher ist es normalerweise in Ordnung, einen Token zB in localStorage zu speichern (auf den auch nur die aktuelle Seite Zugriff hat) und es funktioniert auch nahtlos mit anderen Nicht-Browser-Plattformen (wie nativen mobilen Apps, Server-to-Server usw.) __und websockets__ (Ich kann nicht genug betonen, wie schmerzhaft es ist, Websockets nahtlos und sicher mit HTTP-Cookies zu betreiben - mein Leben ist viel einfacher, seit wir dies nicht mehr versuchen). Im Allgemeinen sollte ein Aktualisierungstoken jedoch widerrufen werden können, da es normalerweise viel langlebiger ist.

Auf jeden Fall wäre ein Pull Request dafür sehr willkommen, ich finde es macht das Ausbügeln der Details viel einfacher.

@jackywxd - Tolle Arbeit, habe einen kurzen Blick darauf
Gibt es irgendeine Möglichkeit, es dem Entwickler zu erleichtern, dies zu implementieren?
Zum Beispiel die von Ihnen erstellten Hooks in die Bibliothek zu integrieren, damit wir die Hooks später nicht hinzufügen müssen ?

Ich denke, Sie sollten einen Pull-Request erstellen und wir könnten die Diskussion dort führen.

@daffl ja stimme zu, besonders mit WS. Und wenn sowohl Client als auch Backend von Ihnen oder Ihrem Team erstellt wurden, ist es am besten, einfach JWT zu verwenden und zusätzliche Abhängigkeiten und Komplexität in Ihrer Anwendung zu vermeiden.
Aber in einigen Fällen, zum Beispiel beim Erstellen einer Storefront-REST-API, die von Drittanbietern / Entwicklern verwendet wird, ist es einfacher, die reguläre Sitzung zu verwenden und die Entwickler zu bitten, ihren Anfragen Anmeldeinformationen hinzuzufügen und dann zu beschreiben, wie der Zugriff abgerufen (und aktualisiert) wird )-Token, speichern Sie sie und übergeben Sie sie bei jeder Anforderung. Was vom Federn-Client perfekt gehandhabt wird, aber in den meisten Fällen, wenn Entwickler nicht mit der Erstellung des Backends vertraut sind, verwenden sie request, superagent, fetch or axios , um ihre Anwendung mit dem Backend zu verbinden. Zumindest in meinem Fall war dies der Hauptgrund, den Storefront-Teil der API so zu verschieben, dass er mit regulären Sitzungen statt direkt mit JWT funktioniert.

Aber wäre das nicht die Designentscheidung und Verantwortung des Herstellers dieser Storefront, diese Funktion in ihre eigene API zu implementieren und diese Funktion dann ordnungsgemäß zu dokumentieren, anstatt diese Entscheidung der Community "aufzuzwingen"?

@TheSinding Ich denke, wir können über weniger häufig verwendete Ansätze "erzwingen" sagen, nicht über Cookies, die (und sind) am häufigsten verwendet wurden, um Benutzersitzungen zu verwalten.

Und ja, es ist eine Designentscheidung, die nach dem Feedback von Entwicklern getroffen wird, die die API verwendet haben. Wenn Sie ein mandantenfähiges System oder eine API ausführen, die von mehreren Teams verwendet wird, ist es manchmal am besten, den allgemeinen Branchenpraktiken zu folgen, um zusätzliche Verwirrung und zusätzlichen Zeitaufwand der Entwickler zu vermeiden, insbesondere wenn alternative Lösungen für den Endbenutzer keine Vorteile bieten.

Um es noch einmal klarzustellen, die Verwendung von JWT ist großartig und viel einfacher zu verwenden, insbesondere für Echtzeit-APIs, und das verwenden wir in 90% der Fälle + Sie müssen keine Redis oder etwas anderes ausführen, um Cookie-Sitzungen zu verwalten.
Aber es gibt einige Ausnahmefälle, in denen es möglicherweise nicht die beste Wahl ist, ich habe in meinem vorherigen Kommentar ein Beispiel für diese Situation angeführt.

Ich habe nicht gesagt, dass du falsch liegst, ich dachte nur "laut" :)

IMO, ich denke, der Ansatz mit JWT wäre ein besserer Ansatz, da dies bereits unterstützt wird und als @daffl auch einfacher zu arbeiten ist

Danke für all Ihr Feedback! JWT vs. Sitzung ist ein anderes Thema, das wir separat besprechen können.

Ich denke, wir sind uns alle einig, dass Zugriffstoken + Aktualisierungstoken eine viel sicherere Lösung sind als nur Zugriffstoken. Es wurde von großen Internetgiganten weit verbreitet. Es ist fair zu sagen, dass die Feathers-Community gerne die Hauptcodebasis von Feathers sehen würde, um integrierte Unterstützung für Aktualisierungstoken bereitzustellen. Tatsächlich hat es mich überrascht, als ich zum ersten Mal erkannte, dass Feathers keine Aktualisierungstoken unterstützt.

Ich habe AWS Cognito in einigen meiner Projekte verwendet, AWS Amplify speichert drei Token in localStorage: ID-Token, Zugriffstoken und Aktualisierungstoken unkomplizierte Benutzeroberfläche, die mit dem Cognito-Backend arbeitet. Ich würde mir ähnliche Entwicklungserfahrungen mit Feathers wünschen.

@TheSinding Danke für deine bisherige großartige Arbeit!

Da die vorhandene Authentifizierung als normaler Dienst implementiert ist, können wir die Unterstützung für Aktualisierungstoken leicht aktivieren, indem wir die Klasse erweitern und paar Hooks hinzufügen:

this.hooks({ after: { create: [issueRefreshToken(), connection('login'), event('login')], remove: [logoutUser(), connection('logout'), event('logout')], patch: [refreshAccessToken()], },

Genauso wie wir die Authentifizierung mithilfe der CLI aktivieren, könnten wir die ähnliche Option in der CLI für die Unterstützung von Aktualisierungstoken anbieten. Der Entwickler kann einfach mit JA antworten, die CLI erstellt dann automatisch einen Kunden-"Refresh-Tokens"-Dienst und aktualisiert die Refresh-Token-bezogenen Konfigurationen in der Standardkonfigurationsdatei. Es ist also wie eine schlüsselfertige Lösung für Entwickler.

@daffl David, danke für die Erstellung von Federn! Ich frage mich nur, ob es für Feathers eine "Beitragsanleitung", "Codierungsrichtlinie", "Stilanleitung" gibt?

Wie bereits erwähnt, wäre eine PR mit der Implementierung für Refresh-Token (oder auch nur einige Ideen) sehr willkommen. Ich hatte noch keine Gelegenheit, die verlinkten Repos zu überprüfen und diese Diskussion wird ziemlich lang. Alles auf dem neuesten Stand und an einem Ort zu haben, würde die Dinge viel einfacher machen. Einige wichtige Stichpunkte:

  • Aktualisierungstoken können ausgestellt werden, indem der Authentifizierungsdienst erweitert wird
  • Aktualisierungstoken sollten in localStorage gespeichert werden
  • Aktualisierungstoken müssen widerrufbar sein (daher muss es eine allgemeinere Implementierung des in https://docs.feathersjs.com/cookbook/authentication/revoke-jwt.html beschriebenen Widerrufsmechanismus geben)
  • Aufgrund der zusätzlichen Komplexität und Einrichtung (wie Redis zum Speichern widerrufener Token) kann es als Funktion verfügbar sein, sollte jedoch nicht standardmäßig aktiviert sein (dh eine standardmäßig generierte App - kann Teil der CLI sein, aber bevor Sie etwas daran tun, Ich suche immer noch wirklich nach Hilfe beim Starten eines hygenbasierten Generators)
  • Informationen zu Beiträgen finden Sie im Leitfaden für Mitwirkende

Wenn jemand refreshToken verwendet, erstellen wir eine Bibliothek, die im Frontend, accessToken und refreshToken wie "Sitzungen" behandelt, zu einfach zu verwenden.

Sie müssen nur die Token übergeben und die Bibliothek versucht, ein neues AccessToken mit refreshToken zu erhalten, wenn es abläuft. Wird derzeit in

Repository: Front-Auth-Handler

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen