Dieses Repository bietet einen Login -Server, der mit dem Cocoda -Mapping -Tool verwendet werden soll. Es ermöglicht Benutzern die Authentifizierung mit verschiedenen Anbietern (z. B. GitHub, Orcid). In https://coli-conc.gbv.de/login/api finden Sie ein Beispiel, wie Sie dies verwenden können.
.envproviders.jsonapplications.jsonLogin-Server benötigt Node.js (> = V18, V20 empfohlen) und Zugriff auf eine MongoDB-Datenbank (> = V5, V7 empfohlen).
git clone https://github.com/gbv/login-server.git
cd login-server
npm install
# after setting up or changing providers, create indexes
npm run indexesLogin-Server ist auch über Docker erhältlich. Weitere Informationen finden Sie in der Dokumentation unter https://github.com/gbv/login-server/blob/master/docker/readme.md.
Wenn Sie den Server hinter einem Reverse-Proxy ausführen, sollten Sie den X-Forwarded-Proto Header aufnehmen, alle HTTP-Methoden zulassen und WebSocket-Proxying aktivieren.
Sie müssen zwei Konfigurationsdateien angeben:
.envSo konfigurieren Sie die Anwendung:
# recommended, port for express, default: 3004
PORT=
# recommended, full base URL, default: http://localhost[:PORT]/
# (required when used in production or behind a reverse proxy)
BASE_URL=
# title of application (will be shown in header)
TITLE=My Login Server
# list of allowed origins separated by comma, includes the hostname of BASE_URL by default
ALLOWED_ORIGINS=
# required for some strategies to enable production mode, default: development
NODE_ENV=production
# strongly recommended, imprint and privacy URLs for footer and clients
IMPRINT_URL=
PRIVACY_URL=
# recommended, secret used by the session
SESSION_SECRET=
# optional, maximum number of days a session is valid (rolling), default: 30
COOKIE_MAX_DAYS=
# threshold in minutes when to send "sessionAboutToExpire" events, default: 60
SESSION_EXPIRATION_MESSAGE_THRESHOLD=
# interval in minutes in which to check for expiring sessions, default: 5
SESSION_EXPIRATION_MESSAGE_INTERVAL=
# username used for MongoDB, default: <empty>
MONGO_USER=
# password used for MongoDB, default: <empty>
MONGO_PASS=
# host used for MongoDB, default: 127.0.0.1
MONGO_HOST=
# port used for MongoDB, default: 27017
MONGO_PORT=
# database used for MongoDB, default: login-server
MONGO_DB=
# the rate limit window in ms, default: 60 * 1000
RATE_LIMIT_WINDOW=
# the rate limit tries, default: 10
RATE_LIMIT_MAX=
# a jsonwebtoken compatible keypair
JWT_PRIVATE_KEY_PATH=
JWT_PUBLIC_KEY_PATH=
# the jsonwebtoken algorithm used
JWT_ALGORITHM=
# expiration time of JWTs in seconds, default: 120, min: 10
JWT_EXPIRES_IN=
# URL for Sources, default: https://github.com/gbv/login-server
SOURCES_URL=
# the path to the providers.json file, default: ./providers.json
PROVIDERS_PATH=
# log = log all messages, warn = only log warnings and errors, error = only log errorsl default: log
VERBOSITY=providers.jsonUm die Anbieter zu konfigurieren. Siehe Anbieter.
applications.json Um dem Benutzer Informationen darüber zu geben, auf welche Anwendungen auf die Daten zugreifen und welche Anwendung die Anmeldung einer Sitzung initiierte, können Sie eine Liste von Anwendungen in applications.json angeben. JSON. Die Liste muss eine Reihe von Objekten sein und jedes Objekt muss eine url und name haben. Beispiel:
[
{
"url" : " https://bartoc.org " ,
"name" : " BARTOC "
},
{
"url" : " https://coli-conc.gbv.de/coli-rich/ " ,
"name" : " coli-rich "
},
{
"url" : " https://coli-conc.gbv.de/cocoda/app/ " ,
"name" : " Cocoda "
},
{
"url" : " https://coli-conc.gbv.de/cocoda/dev/ " ,
"name" : " Cocoda (dev) "
},
{
"url" : " https://coli-conc.gbv.de/cocoda/rvk/ " ,
"name" : " Cocoda (RVK) "
},
{
"url" : " https://coli-conc.gbv.de/cocoda/wikidata/ " ,
"name" : " Cocoda (Wikidata) "
},
{
"url" : " https://coli-conc.gbv.de/cocoda/ " ,
"name" : " Cocoda (other) "
},
{
"url" : " https://coli-conc.gbv.de " ,
"name" : " Other coli-conc application "
}
] Die URL sollte zugänglich sein, da die Schnittstelle darauf verknüpft wird. Eine Sitzung ist einer Anwendung zugeordnet, wenn die Referrer -URL die url der Anwendung enthält. Die Anwendungen werden von oben nach unten überprüft, sodass Sie diese von den meisten spezifischen URLs auf die am wenigsten spezifische URL bestellen sollten (siehe Beispiel oben).
npm run startDer Server bietet eine Weboberfläche, eine HTTP -API und ein WebSocket.
Mit der Weboberfläche können Benutzer Konten mit Verbindungen zu mehreren Identitäten bei Identitätsanbietern erstellen und verwalten (siehe Anbieter). Anbieter werden verwendet, um Benutzer zu authentifizieren, da der Login-Server keine Kennwörter speichert (einzelne Anmelden).
Mit der HTTP-API und der Websocket können Client-Anwendungen mit dem Anmelderver interagieren, beispielsweise zu überprüfen, ob ein Benutzer angemeldet wurde, und herauszufinden, welche Identitäten zu einem Benutzer gehören (siehe Login-Client- und Login-Client-Vue für eine JavaScript-Bibliotheken, um Webanwendungen mit Login-Server zu verbinden).
Der Login -Server kann weiterhin verwendet werden, um Benutzer mit anderen Diensten zu authentifizieren, damit Benutzer ihre Identitäten nachweisen können.
Directory bin enthält ein Helfer -Skript für die Verwaltung einer Serverinstanz wie das Auflisten von Benutzerkonten und das Verwalten lokaler Anbieter.
Tests verwenden denselben MongoDB wie in .env konfiguriert, nur mit dem Postfix -test nach dem Datenbanknamen.
npm test Login-Server verwendet Passport (GitHub) als Authentifizierung Middleware. Passport verwendet sogenannte "Strategien", um die Authentifizierung mit verschiedenen Anbietern zu unterstützen. Eine Liste der verfügbaren Strategien finden Sie hier. Derzeit unterstützte Strategien im Login-Server sind:
Da Strategien in ihren Rückrufen unterschiedliche Parameter verwenden, hat jede Strategie eine eigene Wrapperdatei in den Ordnerstrategien strategies/ . Um eine weitere Strategie zum Anmelderver hinzuzufügen, fügen Sie eine Datei mit dem Namen {name}.js (wobei {name} der Name der Strategie ist, die mit passport.authenticate verwendet wird) mit der folgenden Struktur (GitHub als Beispiel):
/**
* OAuth Stategy for GitHub.
*/
// Import strategy here
import { Strategy } from "passport-github"
// Don't change this part!
export default ( options , provider , callback ) => new Strategy ( options ,
// Strategies have different callback parameters.
// `req` is always the first and the `done` callback is always last.
( req , token , tokenSecret , profile , done ) => {
// Prepare a standardized object for the user profile,
// usually using information from the `profile` parameter
let providerProfile = {
// Required, don't change this!
provider : provider . id ,
// Required: Choose a field that represents a unique user ID for this user
id : profile . id ,
// Optional: Provides a display name (e.g. full name)
name : profile . displayName ,
// Optional: Provides a username
username : profile . username
}
// Call a custom callback. `req`, `providerProfile`, and `done` are required,
// `token` and `tokenSecret` can be null.
callback ( req , token , tokenSecret , providerProfile , done )
} )Sie können sich die vorhandenen Strategien als Beispiele ansehen und Ihre eigene über eine Pull -Anfrage hinzufügen.
Nachdem Sie die Strategie hinzugefügt haben, können Sie sie verwenden, indem Sie einen Anbieter zu providers.json hinzufügen:
[
{
"id" : " github " ,
"strategy" : " github " ,
"name" : " GitHub " ,
"template" : " https://github.com/{username} " ,
"options" : {
"clientID" : " abcdef1234567890 " ,
"clientSecret" : " abcdef1234567890abcdef1234567890 "
},
"image" : " https://upload.wikimedia.org/wikipedia/commons/9/91/Octicons-mark-github.svg " ,
"url" : " https://github.com "
}
]Jedes Objekt in der Liste der Anbieter kann die folgenden Eigenschaften haben:
id (erforderlich) - eindeutige ID für den Anbieter.strategy (Erforderlich) - Name der vom Anbieter verwendeten Passstrategie.name (Erforderlich) - Anzeigename des Anbieters.template (optional) - Eine Vorlagenzeichenfolge zum Generieren eines URI (der Platzhalter {field} kann jedes Feld sein, das im providerProfile -Objekt enthalten ist, normalerweise {id} oder {username} ).credentialsNecessary (optional) - für diesen Anbieter auf true festgelegt, wenn Benutzername und Kennwortanmeldeinformationen erforderlich sind. Anstelle einer Weiterleitung (für OAuth) zeigt Login-Server ein Anmeldeformular an, das die Anmeldeinformationen an einen Post-Endpunkt sendet.options (meist erforderlich) - Ein Optionsobjekt für die Strategie, das häufig Client -Anmeldeinformationen für den Authentifizierungsendpunkt enthält.image (optional) - Ein mit dem Anbieter zugeordneter Bild. Wird auf der Anmeldeseite und in der Liste der verbundenen Identitäten angezeigt. Sie können statische Bilder im Ordner static/ bereitstellen. Der Wert für die Eigenschaft wäre dann static/myimage.svg . Wenn der Dateiname mit der id des Anbieters übereinstimmt, wird das Bild automatisch zugeordnet.url (optional) - eine URL für den Anbieter; wird mit seinem Bild /Symbol unter /account verknüpft. Es gibt Standard -URLs für die Strategien github , orcid , mediawiki und stackexchange . Das Folgende ist ein Beispiel providers.json , das zeigt, wie Sie jeden der vorhandenen Anbieter konfigurieren:
[
{
"id" : " github " ,
"strategy" : " github " ,
"name" : " GitHub " ,
"template" : " https://github.com/{username} " ,
"options" : {
"clientID" : " abcdef1234567890 " ,
"clientSecret" : " abcdef1234567890abcdef1234567890 "
}
},
{
"id" : " orcid " ,
"strategy" : " orcid " ,
"name" : " ORCID " ,
"template" : " https://orcid.org/{id} " ,
"options" : {
"clientID" : " APP-abcdef1234567890 " ,
"clientSecret" : " abcdef1-23456-7890ab-cdef12-34567890 "
}
},
{
"id" : " mediawiki " ,
"strategy" : " mediawiki " ,
"name" : " Mediawiki " ,
"template" : " https://www.mediawiki.org/wiki/User:{username} " ,
"options" : {
"consumerKey" : " abcdef1234567890 " ,
"consumerSecret" : " abcdef1234567890abcdef1234567890 "
}
},
{
"id" : " stackexchange " ,
"strategy" : " stackexchange " ,
"name" : " Stack Exchange " ,
"template" : " https://stackexchange.com/users/{id} " ,
"options" : {
"clientID" : " 12345 " ,
"clientSecret" : " abcdef1234567890(( " ,
"stackAppsKey" : " 1234567890abcdefg(( "
}
},
{
"id" : " my-ldap " ,
"strategy" : " ldapauth " ,
"name" : " My LDAP " ,
"credentialsNecessary" : true ,
"options" : {
"server" : {
"url" : " ldap://ldap.example.com " ,
"bindDN" : " uid=admin,dc=example,dc=com " ,
"bindCredentials" : " abcdef1234567890 " ,
"searchBase" : " dc=example,dc=com " ,
"searchFilter" : " (uid={{username}}) "
}
}
},
{
"id" : " easydb " ,
"name" : " easydb test provider " ,
"strategy" : " easydb " ,
"credentialsNecessary" : true ,
"options" : {
"url" : " https://easydb5-test.example.com/api/v1/ "
}
},
{
"id" : " some-script " ,
"strategy" : " script " ,
"name" : " Some Script " ,
"credentialsNecessary" : true ,
"template" : " https://example.org/some-script/{id} " ,
"options" : {
"script" : " ./bin/example-script "
}
},
{
"id" : " cbs " ,
"strategy" : " cbs " ,
"name" : " CBS " ,
"credentialsNecessary" : true ,
"template" : " cbs:{id} " ,
"options" : {
"url" : " https://example.com/ext/api/colirich/users/info " ,
"apiKey" : " abcdef1234567890 "
}
}
] Um lokale Anbieter zu konfigurieren, verwenden Sie bitte das bereitgestellte Skript unter bin/manage-local.js . Sie können lokale Anbieter erstellen/löschen und Benutzer für lokale Anbieter erstellen/löschen.
Sie können den Pfad an die Datei providers.json mit PROVIDERS_PATH in .env anpassen.
Hinweise zur Verwendung des Medienwiki -Anbieters:
"baseURL": "https://www.wikidata.org/" .https://coli-conc.gbv.de/login/login/wikidata/return für unsere Login-Server-Instanz).Hinweise zur Verwendung des Skriptanbieters:
lib/script-strategy.js ).options.script bereitgestellt.bin/example-script .chmod +x ).id -Wert zurückgeben, wenn die Authentifizierung erfolgreich war. Optional kann name angegeben werden und wird als Anzeigename verwendet.Login-Server bietet JSON-Web-Token an, mit denen man sich mit anderen Diensten authentifizieren kann (wie JSKOS-Server). JSONWEBTOKE wird zur Unterzeichnung der Token verwendet.
Standardmäßig wird ein neuer RSA-Keypair generiert, wenn die Anwendung erstmals gestartet wird (2048 Bit unter Verwendung von Node-RSA). Dies wird standardmäßig in ./private.key und ./public.key verfügbar sein. Sie können die Datei ./public.key an einen anderen Dienst geben, der die Token überprüfen muss. Alternativ wird der derzeit verwendete öffentliche Schlüssel am /um den Endpunkt angeboten.
Sie können auch einen benutzerdefinierten Pfad für die Schlüsseldateien bereitstellen, indem Sie JWT_PRIVATE_KEY_PATH und JWT_PUBLIC_KEY_PATH in .env einstellen. Wenn einer oder beide Schlüssel nicht gefunden werden können, werden die Schlüssel erzeugt. Standardmäßig wird der RS256 -Algorithmus verwendet, aber jeder andere öffentliche Schlüsselalgorithmus kann verwendet werden, indem JWT_ALGORITHM festgelegt wird.
Standardmäßig gilt jedes Token für 120 Sekunden. Sie können dies anpassen, indem Sie JWT_EXPIRES_IN in .env einstellen.
Token werden entweder über den /Token -Endpunkt oder über die WebSocket -Anforderung des Typs zum token empfangen. Darüber hinaus wird ein Token über das Websocket gesendet, nachdem der Benutzer angemeldet ist und dann regelmäßig vor Ablauf des letzten Tokens.
Beispiel, wie Sie ein Token überprüfen:
import jwt from "jsonwebtoken"
// token, e.g. from user request
let token = "..."
// get public key from file or endpoint
let publicKey = "..."
jwt . verify ( token , publicKey , ( error , decoded ) => {
if ( error ) {
// handle error
// ...
} else {
let { user , iat , exp } = decoded
// user is the user object
// iat is the issued timestamp
// exp is the expiration timestamp
// ...
}
} )Alternativ können Sie Passport-JWT verwenden (Beispiel wird folgen).
Zeigt eine Zielseite mit allgemeinen Informationen zum Login -Server an.
Zeigt eine Site an, um das Benutzerkonto zu verwalten (falls bereits authentifiziert) oder leitet sie zu /login (falls nicht authentifiziert).
Zeigt eine Site an, um die Sitzungen des Benutzers zu verwalten (falls authentifiziert) oder leitet sie zu /login (falls nicht authentifiziert).
Zeigt eine Site an, die sich anmeldet (falls nicht authentifiziert) oder an /account anführt (falls authentifiziert).
Wenn der Abfrageparameter redirect_uri angegeben ist, wird die Site nach einer erfolgreichen Anmeldung zum angegebenen URI weitergeleitet. (Wenn der Parameter angegeben ist, aber leer ist, verwendet er den Empfehler als URI.)
Zeigt eine Anmeldeseite für einen Anbieter an. Für OAuth -Anbieter wird diese Seite auf die Seite des Anbieters umgeleitet, um Ihre Identität zu verbinden, die dann zu /login/:provider/return weiterleitet. Für Anbieter, die Anmeldeinformationen verwenden, wird dies ein Anmeldeformular angezeigt.
Diese Seite behandelt auch redirect_uri (siehe /login oben).
Post -Endpunkt für Anbieter mit Anmeldeinformationen. Bei Erfolg wird es zu /account weitergeleitet, andernfalls wird es wieder zu /login/:provider weitergeleitet.
Trennt einen Anbieter vom Benutzer und leitet sie auf /account weiter.
Melden Sie sich den Benutzer von seinem Konto aus. Beachten Sie, dass die Sitzung bestehen bleibt, weil sie für die Websockets verwendet wird. Auf diese Weise kann die Anwendung für die aktuelle Sitzung Ereignisse an Active WebSockets senden, auch wenn der Benutzer sich angemeldet hat.
Zeigt eine Site an, um das Benutzerkonto zu löschen.
Verwenden Sie das Löschen des Benutzerkontos und leiten Sie sie zu /login weiter.
Der Server bietet für jeden OAuth -Anbieter einen OAuth -Umleitungsendpunkt (Umleitung URI).
Rückrufendpunkt für OAuth -Anfragen. Speichert die verbundene Identität vor dem Benutzer (oder erstellt bei Bedarf einen neuen Benutzer) und leitet sie auf /account weiter.
Bevor Sie direkt mit der HTTP-API und der Websocket-API programmieren, sehen Sie sich die JavaScript-Browserbibliothek an der Login-Client-JavaScript an. Es ist hier in Aktion zu sehen (Quelle für diese Seite).
Gibt ein Objekt mit title (Titel der Anmelde-Server-Instanz), env (Umgebung wie development oder production ), publicKey (normalerweise ein öffentlicher RSA-Schlüssel) und algorithm zurück (der verwendete JSONWEBTOKE-Algorithmus). Der entsprechende private Schlüssel zum angegebenen öffentlichen Schlüssel wird bei der Unterzeichnung von JWTs verwendet.
Gibt eine Liste der verfügbaren Anbieter zurück (abgestattet sensible Informationen).
Gibt den aktuell angemeldeten Benutzer zurück. Gibt einen 404 -Fehler zurück, wenn kein Benutzer angemeldet ist.
Gibt einen bestimmten Benutzer zurück. Derzeit beschränkt auf die eigene Benutzer -ID.
Passt einen bestimmten Benutzer an. Kann nur verwendet werden, wenn derselbe Benutzer derzeit angemeldet ist. Zugelassene Eigenschaften ändern sich zu ändern: name (alles andere wird ignoriert).
Entfernt alle Sitzungen für den aktuellen Benutzer, mit Ausnahme der aktuellen Sitzung.
Entfernt die Sitzung mit SessionID :id (muss eine Sitzung für den aktuellen Benutzer sein).
Gibt ein JSON -Web -Token im Format zurück:
{
"token" : " <JWT> " ,
"expiresIn" : 120
}Siehe auch: JWTS.
Das Token selbst enthält eine user (die entweder Informationen über den derzeit angemeldeten Benutzer enthält, oder ist null, wenn der Benutzer nicht angemeldet ist) und eine sessionID -Eigenschaft, die zur Authentifizierung in einer WebSocket -Verbindung erforderlich ist.
Die WebSocket -API an der Basis -URL / sendet Ereignisse zum aktuellen Benutzer oder der aktuellen Sitzung. Ereignisse werden als JSON-kodierte Saiten gesendet, die so aussehen:
{
"type" : " event name (see below) " ,
"date" : " date (as ISOString) " ,
"data" : {
"user" : {
"uri" : " URI of user " ,
"name" : " name of user " ,
"identities" : {
"xzy" : {
"id" : " ID of user for provider xzy " ,
"uri" : " URI or profile URL of user for provider xzy " ,
"name" : " display name of user for provider xzy (if available) " ,
"username" : " username of user for provider xzy (if available) "
}
}
}
}
}open - gesendet, nachdem Websocket -Verbindung hergestellt wurde, verwenden Sie dies anstelle von ws.onopen !loggedIn - gesendet, wenn der Benutzer angemeldet ist (wird sofort nach dem Festlegen des Websocket gesendet, wenn der Benutzer bereits angemeldet ist)loggedOut - gesendet, wenn der Benutzer sich angemeldet hat (wird sofort nach dem Festlegen des Websocket gesendet, wenn der Benutzer nicht angemeldet ist)updated - gesendet, wenn der Benutzer aktualisiert wurde (z. B. eine neue Identität hinzugefügt usw.)providers - gesendet nach der Herstellung von WebSocket -Verbindung (besteht aus einem data.providers mit einer Liste der verfügbaren Anbieter)about - gesendet nach der Herstellung von WebSocket -Verbindung ( data haben das gleiche Format wie in Get /um)token - gesendet, wenn sich der Benutzer angemeldet hat und dann in Intervallen vor Ablauf des vorherigen Tokens ( data haben das gleiche Format wie in GET /Token).authenticated - als Erfolgsantwort gesendet, wenn Sie eine Authentifizierung anfordern (siehe unten)pong - Als Antwort auf eine Anfrage vom Typ ping gesendet (kann verwendet werden, um festzustellen, ob das Websocket abgestanden ist)sessionAboutToExpire - gesendet, wenn die aktuell zugehörige Sitzung abgelaufen isterror - als Antwort auf eine missgebildete Nachricht über das WebSocket gesendet (besteht aus einer data.message . Message mit einer Fehlermeldung)Sie können auch Anfragen an das Websocket senden. Dies müssen auch JSON-kodierte Saiten in der folgenden Form sein:
{
"type" : " name of request "
} Dies ist eine spezielle Anfrage, die ein von GET /Token erworbener JWT verwendet, um das aktuelle WebSocket mit einer bestimmten Sitzung zu verbinden (gesendete Anforderungsobjektbedarfs -Eigenschafts token ).
Die authenticate Anforderung ist manchmal erforderlich, wenn das WebSocket aus einer anderen Domäne als anmeldungserver verwendet wird. In diesem Fall muss ein Token über die API angefordert werden (z. B. mit Fetch mit credentials: "include" oder Axios mit Option withCredentials: true ) und über das WebSocket gesendet werden. Das Token enthält die verschlüsselte Sitzung, die dann mit der Websocket -Verbindung zugeordnet ist. Hier ist ein Beispiel dafür
Das Folgende ist ein einfaches Beispiel zur Verbindung zum WebSocket.
// Assumes server is run on localhost:3005
let socket = new WebSocket ( "ws://localhost:3005" )
socket . addEventListener ( "message" , ( message ) => {
try {
let event = JSON . parse ( message )
alert ( event . event , event . user && event . user . uri )
} catch ( error ) {
console . warn ( "Error parsing WebSocket message" , message )
}
} ) PRS akzeptiert.
dev als Grundlage. Änderungen von dev werden nur für neue Veröffentlichungen in master zusammengeführt.Nur für Betroren
Bitte arbeiten Sie während der Entwicklung an der dev (oder noch besser in einem Feature -Zweig und fusionieren Sie in dev , wenn Sie bereit sind).
Wenn eine neue Version fertig ist (dh die Funktionen werden fertiggestellt, in dev zusammengeführt und alle Tests erfolgreich sein), führen Sie das mitgelieferte Release -Skript aus (ersetzen Sie "Patch" durch "Minor" oder "Major", falls erforderlich):
npm run release:patchDies wird:
dev sinddev auf dem neuesten Stand istnpm version patch (oder "Minor"/"Major") ausdev drückenmasterdev zusammenführenmaster mit Tags drückendevNach dem Ausführen erstellen GitHub -Aktionen automatisch einen neuen GitHub Release -Entwurf. Bitte bearbeiten und veröffentlichen Sie die Version manuell.
MIT © 2019 Verbundzentrale des GBV (VZG)