Color scheme: Riviera
You are here: start » en » Documentation » Data integration and data access » WKL Database Gateway » Reference » HTTP: sign-in (reference)

HTTP: sign-in (reference)

All sign-in methods of the service with their rules and responses, self-service for your own keys, and the sign-in policy with the second factor. The model behind it is explained in Sign-in and identity pass-through.

Syntax

ItemMeaning
X-Api-Key: <service key or personal key>first factor; header name per Security:ApiKeyHeader
Authorization: Negotiate …Windows account
Authorization: Basic …SQL Server login, or personal key as password
X-Wkl-Session: <session>second factor, in addition to the first
X-Wkl-Role: <identity>choice of the database identity
POST /auth/v1/totp[/{alias}]code for session
POST /auth/v1/enrol[/{alias}]set up the second factor at first sign-in
GET /self/v1/[{alias}/]keysself-service: own keys
POST /self/v1/[{alias}/]issue-keyself-service: issue a key
POST /self/v1/[{alias}/]revoke-keyself-service: revoke a key

Parameters

Sign-in with the Windows account

Besides the service key, a caller can sign in with their Windows account, via HTTP Negotiate—Kerberos or, where Kerberos isn't available, NTLM. This is intended for people: Access via the ODBC driver, and Excel or Power BI, sign in as the user who runs them. No key or password is stored anywhere, and the audit names the person instead of a key name.

"Security": {
  "AuthMode": "ApiKey, Windows",
  "ApiKeys": [ { "Client": "fachanwendung", "Key": "env:FACHANWENDUNG_SCHLUESSEL" } ],
  "WindowsAccounts": [
    { "Account": "CORP\\BusinessData-Readers", "Client": "fachdaten-lesen" },
    { "Account": "CORP\\BusinessData-Editors", "Client": "fachdaten-pflege" },
    { "Account": "S-1-5-32-545",               "Client": "auskunft" }
  ],
  "Clients": [
    { "Id": "fachanwendung",    "Databases": [ "fachdaten" ], "Resources": [ "*" ],     "Verbs": [ "*" ] },
    { "Id": "fachdaten-lesen",  "Databases": [ "fachdaten" ], "Resources": [ "views" ], "Verbs": [ "GET" ], "ReadOnly": true },
    { "Id": "fachdaten-pflege", "Databases": [ "fachdaten" ], "Resources": [ "*" ],     "Verbs": [ "*" ] },
    { "Id": "auskunft",         "Databases": [ "fachdaten" ], "Resources": [ "views" ], "Verbs": [ "GET" ], "ReadOnly": true }
  ]
}
RuleReason
AuthMode lists the methods, comma-separated: ApiKey, Windows, SqlLogin, PersonalKey in any combinationServices keep their key, people sign in with Windows or with a SQL Server login
An entry under WindowsAccounts gives an account or a group the rules of a caller under ClientsRights are attached to groups, as elsewhere in Windows; a person gets them through their membership
The rights of several entries add up; a read-only rule doesn't restrict another oneThat's how groups work everywhere in Windows. Anyone who should only read doesn't belong in a group that may write
Comparison is by SID; a name is translated into it at startupA renamed group keeps its rights. A name that doesn't exist prevents startup instead of silently granting nothing
Specify built-in groups by their SID (S-1-5-32-545 instead of VORDEFINIERT\Benutzer, the German name of BUILTIN\Users)Their names depend on the language of the Windows installation. A SID is checked only for its form
A service key sent along decides aloneAn explicit proof isn't overruled by an implicit one; a wrong key results in 401, even if a Windows sign-in comes along
A signed-in person without a matching entry reaches nothing, like a key without a ruleThe audit names who tried it. A 401 would make Office ask again for credentials that the person has long since presented
Groups that Windows lists only for denying don't countFor example, the Administrators group of a non-elevated user; counting it would grant what Windows withholds

Without sign-in, the service responds with 401 and WWW-Authenticate: Negotiate; only then does a client sign in with Windows. Every new connection therefore begins with a request without sign-in, which appears in the audit as anonym … UNAUTHENTICATED (401). That's the first step of the method, not an error. After that, the sign-in applies to the whole connection.

Kerberos requires an SPN HTTP/<host-name> for the account under which the service runs; if it's missing, Windows falls back to NTLM.

Sign-in with a SQL Server login (HTTP Basic)

If you have been reaching SQL Server with a name and password, you sign in to the service with the same login: via HTTP Basic, that is, with name and password in the Authorization header. The service doesn't check the password itself and doesn't store one. It opens a connection to the server of the addressed alias with the name and password, and SQL Server decides, as with a direct connection. All statements of the request then run over connections of this login: permissions, column defaults and triggers see the login itself, including ORIGINAL_LOGIN().

"Databases": [
  { "Name": "fachdaten", "Provider": "sqlserver", "ConnectionString": "…",
    "AllowSqlLogins": true }
],
"Security": {
  "AuthMode": "ApiKey, Windows, SqlLogin",
  "SqlLogins": [
    { "Login": "*",       "Client": "fachdaten-lesen"  },
    { "Login": "pflege1", "Client": "fachdaten-pflege" }
  ],
  "TrustedProxies": [ "10.0.0.5" ]
}
RuleReason
AuthMode contains SqlLogin, and an alias accepts SQL Server logins only with AllowSqlLoginsWhich database checks the name and password is a decision per alias. An alias without the switch responds to a SQL Server login as to an unknown one (401); the response doesn't reveal which aliases exist
Server and database come from the alias's connection string; name and password only replace its loginThe caller doesn't choose the database, as everywhere in the service
An entry under SqlLogins gives a login (Login: its name, or * for every login) the rules of a caller under Clients; the rights of several entries add upThe rules remain the outer barrier: which aliases, resource types and verbs can be reached over HTTP. Inside, the login's rights in SQL Server decide
The check happens before rules and catalog are consultedOtherwise someone who knows a name but not the password could tell from the responses which objects exist
Only over a protected connection: HTTPS, a proxy from TrustedProxies that reports X-Forwarded-Proto: https, or a request from the same computer. Otherwise 403, without trying the passwordBasic sends the password with every request. The X-Forwarded-Proto of an arbitrary client doesn't count, and a trusted proxy on the same computer doesn't turn a request from outside into a local one
At most 5 failed attempts per login and alias, and 20 per origin, within one minute; then 429, even with the correct password. An attempt the server gives no readable answer to (503) also counts as failedSQL Server counts failed attempts only for logins with a password policy, and the service's rate limiting applies only after sign-in. Even without a readable answer, the server may have checked the password
The password appears neither in the log nor in the text form of the callerIt's held only in the request's memory and in the connection pool; SqlClient keeps a separate pool per login
A service key sent along takes precedence over Basic, Basic over Windows; a rejected password results in 401 without falling back to WindowsAn explicit proof isn't overruled by an implicit one
Without sign-in, the 401 response names every enabled method: WWW-Authenticate: Negotiate and WWW-Authenticate: Basic realm="Wkl.Data.Gateway", charset="UTF-8"A client picks what it can do; charset tells it that name and password are read as UTF-8
ResponseMeaning
401 “Es wurde keine gültige Anmeldung vorgelegt.” (No valid sign-in was presented.)Name or password wrong, login unknown or disabled, or the alias doesn't accept SQL Server logins. The response deliberately doesn't distinguish these; the log names the SQL Server error number (such as 18456)
403 “SQL-Anmeldungen nimmt der Dienst nur über eine verschlüsselte Verbindung an.” (The service accepts SQL Server logins only over an encrypted connection.)The password came unprotected from another computer
403 “Diese Anmeldung hat keinen Zugang zur Datenbank des Alias.” (This login has no access to the alias's database.)SQL Server accepted the login but didn't open the alias's database (4060)
403 “Die Datenbank verweigert dieser Anmeldung den Vorgang.” (The database refuses this operation for this login.)The login lacks a permission (error 229, 230, 262, 297, 300 or 916)
429 “Zu viele fehlgeschlagene Anmeldungen; bitte später erneut versuchen.” (Too many failed sign-ins; please try again later.)The limit for failed attempts has been reached

Personal keys

A personal key belongs to a person, not to a service. It requires no domain, no SQL Server password and no infrastructure, and so it works everywhere the service can be reached. People and keys are kept in the administration store; the method needs it and is turned on with PersonalKey:

"Security": { "AuthMode": "ApiKey, PersonalKey" },
"Store": { "Path": "store/dbgwsvc.db", "MasterKey": "env:WKL_GW_MASTER_KEY", "MaxKeyLifetimeDays": 365 }

The key goes into the same header as a service key (X-Api-Key, see ApiKeyHeader). Every client that can send a service key—the ODBC driver with APIKEY/APIKEYENV, a script, a reporting tool—sends a personal one without any change. The prefix distinguishes the two. A personal key also works as the password of HTTP Basic (the name isn't checked; pkuser is recommended, see below): Excel and Power BI store such credentials (authentication kind “Basic”) in their own credential management, whereas a header would have to be written into the query—and therefore into every workbook. The form of the key distinguishes it from the password of a SQL Server login.

<a id=“user-name-pkuser”></a>User name pkuser (convention). If you enter a personal key as the password, enter pkuser as the user name (personal key user). This isn't required: the service recognizes the key by its form (wkl_…), not by the name, and accepts any name. A uniform name still has advantages:

  • In the credential managers of Excel, Power BI and Windows, you can see at a glance that this entry holds a personal key and not a SQL Server login.
  • On the phone, “user name pkuser, password your key” is enough, without explaining which name is meant.
  • Nobody enters the name of another system and then mistakes it for part of the access.

Don't create a pkuser login in SQL Server: the service checks a password that doesn't have the form of a key as a SQL Server login of this name.

PropertyDefinitionReason
Formwkl_ + 43 random characters (256 bits, letters and digits) + 6 check characters, 53 characters in totalA secret scanner finds a fixed prefix and length in source code and logs; the check characters detect a typo without a lookup
Storageonly the SHA-256 of the key, a public ID (k + 8 characters) and the last 4 charactersA copy of the file opens no request. With 256 bits of randomness, SHA-256 is enough; a slow password hashing method would cost time on every request and protect nothing
Displayonce, when issuedAfter that, only the holder has it
Rightsthe rules (Clients) assigned to the person; the key restricts and never grants anything itself: aliases, read-only, networks (From/ExceptFrom)A key for Excel on someone else's computer gets only what it needs, without its own rule
Lifetimeexpiry date per key; if not specified, the maximum lifetime Gateway:Store:MaxKeyLifetimeDays (default 365, 0 = no expiry); longer is rejectedA forgotten key on an old computer stops working on its own
Revocationrevoking a key or disabling a person takes effect from the next request, without a reload and without a restartThe key is looked up in the store on every request (an index lookup on the local file); there's no cache from which a revoked key could continue to work
Several per personyes, per device or purpose, with a labelA lost laptop costs one key, not all of them
Evidenceaudit: Person (Kennung) (person (ID)); “last used” with time and origin per keyWho acted and which key to revoke
Identity in SQL ServerIf the person has their own login (--login) and the alias has Persons: Impersonate (which requires Windows in AuthMode), the request runs under that login (EXECUTE AS LOGIN, DenyImpersonate applies). Otherwise under the alias login. In both cases the person is in the session context, read-only: SESSION_CONTEXT(N'wkl.person'), SESSION_CONTEXT(N'wkl.key')Triggers, column defaults and views can ask who acted, even without a login of their own. The connection pool resets the context before the next request
Service keysnever switch identityA service isn't a person

“Last used” is collected and written every 60 seconds and on shutdown, not per request: every write to the file would make the service read it again. A crash costs at most that minute of statistics, never an authorization. The service doesn't treat writes that don't change the operating data (usage, keys, people) as a new version and doesn't report them.

Manage people and keys—via the administration API, or on the service's computer with the commands save-person, issue-key, revoke-key and list-keys; they run with the configuration and environment of the service, even while it's running. Invocation, options and return codes are listed under Server commands: people and keys. These commands don't write to the change log (whoever runs them has access to the file anyway); the recovery commands init-admin, reset-totp and remove-sign-in-rule, however, do, with the actor “Konsole (Rechner, Konto)” (console (computer, account)).

ResponseMeaning
401 “Der persönliche Schlüssel ist unbekannt.” (The personal key is unknown.)No such key, or a typo
401 “Persönliche Schlüssel sind an diesem Dienst nicht eingerichtet.” (Personal keys aren't set up on this service.)PersonalKey isn't turned on in AuthMode
403 “Die Person zu dieser Anmeldung ist gesperrt.” (The person for this sign-in is blocked.)The Windows account or SQL Server login belongs to a blocked person. If the same account belongs to several people, the service doesn't check this
401 “… ist abgelaufen.” (… has expired.) / “… ist gesperrt.” (… is revoked.) / “… ruht; er gilt nur, solange die Verwaltung ihn freigeschaltet hat.” (… is dormant; it's valid only while an administrator has activated it.) / “Die Person zu diesem Schlüssel ist gesperrt.” (The person for this key is blocked.)State of the key or of the person. Only someone who has the key learns this distinction; guessing a key is hopeless
403 “Dieser persönliche Schlüssel gilt nicht für diese Herkunft.” (This personal key isn't valid for this origin.)The key names networks, and the request comes from outside them—or its origin can't be determined
403 “Der Zugriff auf diese Ressource ist nicht freigegeben.” (Access to this resource isn't permitted.)Rules of the person or restriction of the key (alias, read-only)
403 “Die SQL-Server-Anmeldung dieser Person gibt es nicht.” (This person's SQL Server login doesn't exist.)--login names a login that SQL Server doesn't know; no fallback to the alias login

On-demand keys

A key can be issued so that it's dormant until an administrator activates it for a period—for example a read-only key without a second factor that a person stores in Power BI for presentations to the board. The key itself stays the same across all activations, so Power BI doesn't need to be set up again. Outside an activation it opens nothing, even if it has been left behind in a report, on a laptop or in a mail.

StepHow
Issueissue-key with "onDemand": true; the key is dormant from the moment it's issued
Activateactivate-key { "id", "hours" }; ends on its own. A new activation replaces the running one
End earlydeactivate-key { "id" }; takes effect with the next request
Maximum durationmax_activation_hours (set Settings), otherwise Gateway:Store:MaxActivationHours, default 12, 1 to 168. Never beyond the key's expiry date

Only an administrator may activate it, not the person themselves; both are recorded in the change log with the actor and the end. The maximum lifetime of the key (MaxKeyLifetimeDays) still applies: after it, a new key is needed.

Self-service

A person who can sign in another way—with their Windows account or their SQL Server login—issues personal keys to themselves, for example for Excel on a laptop, and revokes them again. Administrators don't need to do anything for this except turn self-service on once.

AddressBodyResponse
GET /self/v1/keys—200 { person, keys: [...], maximumDays }; per key ID, label, last 4 characters, read-only, aliases, issue date, expiry, revocation, last use—never the key
POST /self/v1/issue-key{ "label", "expiresAt"?, "readOnly"?, "databases"? }201 with key, once; Cache-Control: no-store
POST /self/v1/revoke-key{ "id" }200; the service answers someone else's key or an already revoked one with 404, without revealing whether it exists
…/self/v1/{alias}/…as abovethe same for a SQL Server login: SQL Server checks its password at a data source, so the address names it
RuleReason
Only with a Windows account or SQL Server login, assigned via the person's own account or own login (Persons.WindowsAccount, Persons.SqlLogin); a group isn't a personA personal key doesn't issue keys: otherwise a stolen one could renew itself beyond its expiry and revocation. A service key belongs to no person
Off until an administrator sets self_service_key_days (set Settings) or Gateway:Store:SelfServiceKeyDays to more than 0Whether people may create access for themselves is decided by administrators
A self-issued key always expires: if not specified, after self_service_key_days, at most after the shorter of this value and MaxKeyLifetimeDaysOnly administrators issue keys without expiry
Can be restricted to read-only and to aliases; networks and the exemption from the second factor can't (400)Both are decisions for administrators
The rights are the person's rules, as with every personal keySelf-service doesn't extend anything
The sign-in policy treats self-service as administration (scope Admin); a new store requires a second factor thereWhoever issues access is administering—their own access. With the default rule, the person needs a second factor that has been set up and a session (/auth/v1/totp, for a SQL Server login /auth/v1/totp/{alias})
Only over a protected connection, like the administration; issuing and revoking are recorded in the change log as Person (Selbstbedienung) (person (self-service))The response carries a key

“WklGatewayClient: VBA class for the data service” (documentation of the administration app) provides OwnKeys, IssueOwnKey and RevokeOwnKey for this.

Sign-in policy and second factor

The sign-in policy defines which sign-in method needs a second factor or is refused for which part of the service from which networks or countries—for example “personal keys from the corporate network without, from outside only with TOTP”, “SQL Server logins never from outside” or “data access only from Germany”. It's kept in the administration store and maintained via the sets SignInRules, SignInRuleItems, SignInRuleExceptions, SignInRuleExceptionItems and Settings of the administration API; without a store, only denials are possible (Gateway:Security:SignInRules in the configuration file).

Field of a ruleValuesMeaning
ScopeAll (default), Data, Adminpart: data interfaces or administration
MethodAny (default), ApiKey, PersonalKey, Windows, SqlLoginsign-in method of the caller
EffectRequireTotp (default), Denysecond factor required, or denial
From / ExceptFromnetworks such as 10.0.0.0/8, or the name of one of your own networks from `Gateway:SignInLog:Networks`origin; empty = any. A name stands for every network listed under it there; an unknown name stops startup. An origin that can't be determined falls within the scope of every rule
Persons / ExceptPersonspeople (name; in the store the Id, attribute person/except_person)only for these people, or not for them; empty = every caller. Requires the administration store
WindowsGroups / ExceptWindowsGroupsWindows groups (name or SID)only for members, or not for them; known only with sign-in by Windows account. An unknown group stops startup
Countries / ExceptCountriesISO 3166-1 alpha-2 country codes such as DE, TH (not case-sensitive)only for requests from these countries, or not for them; empty = any country. A country that can't be determined falls within the scope of the rule. A code that isn't two letters stops startup
Operators / ExceptOperatorsnetwork operators as autonomous systems, such as AS3320 (also 3320)only for requests from the networks of these operators, or not for them; empty = any. An operator that can't be determined falls within the scope of the rule (see By network operator, program and time)
Clients / ExceptClientskind of program, such as Power Query, WinHTTP, Browser, PowerShellonly for requests that state this program, or not for them; empty = any. Claimed by the client; a request without a User-Agent falls within the scope of the rule
Hours / ExceptHourstime windows such as Mon-Fri 06:00-20:00, Sat,Sun, 22:00-06:00only at these times, or not at them; empty = at any time. The service's clock, in the zone of Gateway:SignInLog:TimeZone. A window that can't be read stops startup
Exceptionslist of exceptions (see below)requests that meet all attributes of an exception at once are spared from the rule, optionally until an end
Labeltextnote, no effect
ModeEnforce (default), Reportenforce or only try. A rule on trial is judged at every sign-in but changes no decision; the sign-in event records what it would have done (see Try a rule)

For individual people or groups. A rule can be restricted to people or Windows groups, or can exclude them—for example “nobody from outside, except John Doe”: { Scope: Data, Effect: Deny, ExceptFrom: [10.0.0.0/8], ExceptPersons: [John Doe] }. The service recognizes a person by their personal key, their own Windows account or their SQL Server login; a group only with a Windows sign-in. A caller who isn't assigned to a person (service key) falls under no such rule and no such exception. explain-sign-in of the administration API shows what a rule means for a person.

By country. The service reads the country of a request from the geolocation database of the sign-in log—in memory, without network access, and only when a rule names countries. A request has no country when no geolocation database is loaded, when it comes from this machine, from one of your own networks (`Gateway:SignInLog:Networks`) or from a private network, or when the database doesn't know the address. Such an unknown country falls within the scope of every rule: “data access only from Germany” refuses it. So exclude your own networks by their addresses, not by a country:

{ "Scope": "Data", "Effect": "Deny", "Label": "Germany only",
  "ExceptCountries": [ "DE" ], "ExceptFrom": [ "10.0.0.0/8" ] }

Important: A country derived from an address is a hint, not proof. A VPN, a proxy or a mobile network moves the origin to another country, and geolocation databases are wrong for some networks. Rules by country add a layer to keys, networks and the second factor; they don't replace any of them.

By network operator, program and time

A rule can also ask for the network operator of the origin, for the program and for the time of day. The three attributes differ in how far you can rely on them:

AttributeWhere the value comes fromReliabilityUnknown when
Network operatorASN file of the geolocation database of the sign-in log, read only when a rule names operatorsobserved: the service derives it from the address; mostly reliable, a VPN moves itno ASN file loaded, own or private address, address unknown
ProgramUser-Agent of the request, grouped as in the sign-in log (Power Query, WinHTTP, Browser, PowerShell, otherwise the first word)claimed: any program can send any text; Excel and Power BI Desktop send the same oneno User-Agent (the ODBC driver sends none)
Timethe service's clock, in the zone of Gateway:SignInLog:TimeZone (empty: the server's zone)observednever

Like an unknown country, an unknown operator or a missing program falls within the scope of the rule and meets no attribute of an exception. A claimed program is therefore fit for narrowing (“the administration only from WinHTTP” refuses a browser). As the only attribute of an exception, it spares anyone who sends the text; combine it there with people, networks or an end.

Write a time window as [days] [hh:mm-hh:mm]:

  • Days Mon, Tue, Wed, Thu, Fri, Sat, Sun as a list (Sat,Sun) or a range (Mon-Fri, also Fri-Mon); without days, every day applies.
  • Times with an exclusive end; 24:00 is the end of the day; without times, the whole day applies.
  • A window across midnight (Mon-Fri 22:00-06:00) belongs to the day it begins on: Friday 10 p.m. to Saturday 6 a.m. is inside it, Monday 3 a.m. isn't.

“SQL Server logins only Monday to Friday, 6 a.m. to 8 p.m.”:

{ "Method": "SqlLogin", "Effect": "Deny", "Label": "SQL office hours only",
  "ExceptHours": [ "Mon-Fri 06:00-20:00" ] }

“From other network operators only with a second factor, never from Plant 3”:

{ "Scope": "Data", "Effect": "RequireTotp", "Label": "code from other networks",
  "ExceptOperators": [ "AS3320" ], "ExceptFrom": [ "Plant 3" ] }

Your own and private networks have no network operator. Without ExceptFrom, the rule would therefore require the code in the plant as well.

The sign-in event names in Signals the network operator it judged by (operator), the program (client) and, under trust, how reliable each signal is (observed, claimed, verified).

Exceptions with an end. The lists ExceptPersons, ExceptCountries and ExceptFrom each exclude on their own and permanently: ExceptPersons: [John Doe] spares John from every country. An exception in Exceptions, by contrast, spares only when all of its attributes apply at once—for example “John Doe from Thailand until November 2”:

{ "Scope": "Data", "Effect": "Deny", "Label": "Germany only",
  "ExceptCountries": [ "DE" ], "ExceptFrom": [ "10.0.0.0/8" ],
  "Exceptions": [ { "Persons": [ "John Doe" ], "Countries": [ "TH" ],
                    "Until": "2026-11-02T00:00:00Z", "Label": "vacation" } ] }
Field of an exceptionValuesMeaning
Personspeople (name; in the store the Id)the caller is one of them; empty = anyone
WindowsGroupsWindows groups (name or SID)the caller is a member of one of them; empty = anyone
Countriescountry codes as abovethe request comes from one of them; empty = any country
Fromnetworks such as 203.0.113.0/24, or names of your own networksthe origin lies in one of them; empty = any
Operatorsnetwork operators such as AS3320the origin belongs to one of them; empty = any
Clientskind of program as abovethe request states one of them; empty = any. Claimed
Hourstime windows as abovethe request falls into one of them; empty = at any time
Untilpoint in time in UTC, such as 2026-11-02T00:00:00Zfrom this point on, the exception spares nobody; empty = no end. A value without a time zone is read as UTC
Labeltextnote, no effect
  • At least one attribute or an end. An exception with neither would spare everyone permanently; the startup check and the administration API reject it.
  • Not knowing never spares. An unknown country, an unknown origin, an unknown network operator, a missing program or a caller who isn't assigned to a person meets no attribute that asks for it. Without a geolocation database, an exception by country therefore closes instead of opening the rule.
  • The end takes effect immediately. The service compares it with its clock on every request; no change is needed. When it notices the end, it writes it once into the change log (Entity exception-lapsed, actor Dienst (Ablauf), before: rule, label and end). An exception that has ended stays in place until it's removed.

Caution when creating a denial. A rule takes effect as soon as it's saved. If you first create a denial for all scopes without exceptions and add the attributes afterward, you lock yourself out at that moment. So create denials with save-sign-in-rule of the administration API, which saves the rule and exceptions in one change; if it has happened, remove-sign-in-rule on the server helps.

Every matching rule counts, a denial before anything else; the order means nothing. Exception: the service judges the exchange of a code and the setup of the factor (/auth/v1/totp, /auth/v1/enrol) only by rules with scope All; a denial only for Data or Admin doesn't prevent an attempt there. And SQL Server checks a SQL Server login before the policy judges: from a denied origin, a wrong password results in 401, a correct one in 403—so an attacker learns whether a password is correct, limited only by the failed-attempt limit. If no rule matches, the rights alone decide. Unauthenticated operation in the development environment isn't judged; a policy that has locked out the administration is repaired with the server commands. A new store starts with the rule “administration only with a second factor” (Scope: Admin, Effect: RequireTotp); it can be changed or deleted like any other. Independently of this, the fixed lower bound Gateway:Store:RequireAdminTotp (default true) requires the second factor for the administration and self-service, for every sign-in method and every origin; not even a key with TotpExempt lifts it. It can only be turned off in the configuration file. The startup check rejects RequireTotp for service keys—a service isn't a person and has no second factor; write Deny instead.

Second factor (TOTP). Time-based codes according to RFC 6238 (6 digits, 30 seconds), as generated by every authenticator app. The factor belongs to a person: a personal key names them; a Windows account and a SQL Server login find them via their own account (WindowsAccount) or their own login (SqlLogin); only the login name is compared, case-insensitively, across all aliases and servers, and a session opened this way applies not only to the alias where the code was exchanged. A group isn't a person; a sign-in that matches no person or two people gets 403 if a second factor is required. An administrator sets it up (enroll-totp), ideally as a one-time link: the page shows the otpauth:// address as a QR code and next to it the key for manual entry. The secret is stored sealed in the store (its own purpose, its own master key change via reseal-store).

Set up at first sign-in. If a person doesn't have a second factor yet but the policy requires one, they may set it up themselves for a limited time—with their first factor, which until then is valid only for this:

POST /auth/v1/enrol[/{alias}]   first factor as when exchanging a code
Body:  { }                      200: { "person", "secret", "uri", "qrPng", "openUntil" }
Body:  { "code": "123456" }     201: as for the exchange - the factor is set up, a session opened
  • When. Within totp_enrol_hours (set Settings, otherwise Gateway:Security:TotpEnrolHours, default 24, at most 168, 0 turns off setup at sign-in) after the key with which the request comes was issued; for a person without a key (Windows account), until the time set by init-admin or reset-totp on the server.
  • Why a deadline. Until the factor is set up, the key alone stands for the person. Anyone who intercepts it during this time could bind their own authenticator app. The deadline keeps this time short; a key that has waited too long must be reissued.
  • Once. The confirmation ends the period and is recorded in the change log; a person who has a factor is no longer offered setup. If someone else set it up first, the rightful holder notices it this way. The administration then sets up a new factor.
  • Two steps. Without a code, the service generates a secret and responds with the address, the key for typing in and a QR code as PNG; it's stored sealed only with the first matching code. A new call without a code replaces the generated secret.
  • Until then, the service answers every other request of the person with 401 and WWW-Authenticate: Wkl-Totp realm="Wkl.Data.Gateway", enrol="/auth/v1/enrol". The administration application then shows the setup card; the VBA class WklGatewayClient reports gwErrEnrolmentRequired and offers BeginTotpEnrolment/ConfirmTotpEnrolment.

Code for session. A code lives for 30 seconds; a driver paging through a table can't ask for one with every request. The code is therefore exchanged once for a session, which then accompanies every request in the X-Wkl-Session header:

POST /auth/v1/totp            first factor: personal key or Windows account
POST /auth/v1/totp/{alias}    first factor: SQL Server login (Basic), checked at this alias
Body:  { "code": "123456" }                  optionally with "role": "vertrieb"
201:   { "session": "…", "id": "t…", "person": "…", "header": "X-Wkl-Session", "expiresAt": "…" }

If the body names role, the session is bound to this database identity: a request without its own choice runs under it. The choice is checked on every request, not at the exchange.

  • The session isn't a sign-in. Every request still carries its first factor; the session applies only to the person, the method and—with a key—the key with which it was opened. A stolen token alone opens nothing. That's why it's a separate header instead of Authorization: Bearer: with Windows and Basic, that header already carries the first factor.
  • Lifetime totp_session_hours (set Settings, otherwise Gateway:Security:TotpSessionHours), default 10 hours, 1 to 168.
  • End: expiry; Revoked in SessionTokens; revoking the key; disabling the person; setting up again or removing their second factor. This is looked up on every request.
  • A code is valid once (its time step must be later than the last one accepted) and one step (30 seconds) before or after the service's clock; this tolerance is fixed and can't be configured. Five wrong codes per person or twenty per origin within 15 minutes lead to 429. The counters are kept in memory: a restart resets them, and anyone who knows a name can lock the person out for a while with wrong attempts.
  • The exchange only over a protected connection (HTTPS, trusted proxy with https, same computer).

Clients without a dialog—Power BI, Excel, scripts—can't ask for a code. For them there are keys with TotpExempt: exempt from a required second factor, not from a denial, and permitted only as a read-only key. With rights that restrict the person to released views, and optionally a network binding, this is the narrowly limited access for reports.

ResponseMeaning
401 with WWW-Authenticate: Wkl-Totp realm="Wkl.Data.Gateway", endpoint="/auth/v1/totp"Second factor required: no session or no valid session. With a SQL Server login, endpoint names the alias (/auth/v1/totp/{alias}). The ODBC driver asks for the code in a dialog and keeps the session for the process
401 “Der Code ist falsch oder wurde schon benutzt.” (The code is wrong or has already been used.)exchange refused
403 “Die Anmelderichtlinie lässt dieses Anmeldeverfahren von dieser Herkunft nicht zu.” (The sign-in policy doesn't allow this sign-in method from this origin.)rule Deny. The response names no country; the sign-in log records a denial that rests on rules with countries alone with the reason deniedByCountry, otherwise denied
401 with WWW-Authenticate: Wkl-Totp realm="Wkl.Data.Gateway", enrol="/auth/v1/enrol"Second factor required but not set up yet; the person may set it up now
403 “… gehört zu keiner Person” (… doesn't belong to any person) / “… ist keiner eingerichtet, und die Frist … ist abgelaufen.” (… none is set up, and the period … has expired.)second factor required but not possible
403 “Ein zweiter Faktor wird nur über HTTPS oder vom eigenen Rechner angenommen.” (A second factor is accepted only via HTTPS or from the same computer.)exchange over an unprotected connection
429too many wrong codes

Try a rule

Try a new rule—above all a denial—first with Mode = Report. The rule is then judged at every sign-in like an enforced one but changes nothing: whoever would be admitted without it stays admitted. The sign-in event names it in Rules with "mode": "report"; SignInRuleReports counts, per rule and version, how many sign-ins it would have hit, from how many people and origins, and which ones an exception would have spared (see administration). explain-sign-in shows it separately under tried.

If the result fits, enforce the rule with save-sign-in-rule and "mode": "Enforce"; from then on it takes effect. The switch is in the change log and changes the rule's version, so events from the trial and from operation stay distinguishable. The administration's lower bound (RequireAdminTotp) is no rule and can't be tried.

See also

en/wiki/data-conectivity/database-gateway/reference/sign-in.txt · Last modified: by Wolfgang van der Stille