Skip to content

Commit 27cae7c

Browse files
Initial draft of LDAP
1 parent b6ac2c1 commit 27cae7c

3 files changed

Lines changed: 151 additions & 6 deletions

File tree

docs/specs/account-linking.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Account Linking
22

3+
TODO(ldap): Specify how an LDAP identity take part in Account Linking.
4+
35
- [Introduction](#introduction)
46
- [Configuration](#configuration)
57
- [Defining how the linking occurs and the corresponding action](#defining-how-the-linking-occurs-and-the-corresponding-action)

docs/specs/ldap.md

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
- [LDAP](#ldap)
2+
* [Supported LDAP protocol](#supported-ldap-protocol)
3+
* [Authenticate Authgear as a LDAP client](#authenticate-authgear-as-a-ldap-client)
4+
* [Configuration of LDAP servers](#configuration-of-ldap-servers)
5+
* [The database schema of a LDAP identity](#the-database-schema-of-a-ldap-identity)
6+
* [Handling of a LDAP identity](#handling-of-a-ldap-identity)
7+
* [The UX of LDAP in Auth UI](#the-ux-of-ldap-in-auth-ui)
8+
9+
# LDAP
10+
11+
This document describes the LDAP support in Authgear. Authgear acts as a LDAP client, and connects to one or more LDAP servers.
12+
13+
## Supported LDAP protocol
14+
15+
The supported LDAP protocol is LDAPv3, which is specified in [RFC4511](https://datatracker.ietf.org/doc/html/rfc4511).
16+
17+
Authgear MUST BE compatible with
18+
19+
1. A LDAP server without TLS (i.e. ldap://)
20+
2. The non-standard ldaps://
21+
3. The StartTLS operation, as defined in [Section 4.14 in RFC4511](https://datatracker.ietf.org/doc/html/rfc4511#section-4.14)
22+
23+
When the connection URL starts with `ldap://`, StartTLS will be tried.
24+
If the LDAP server responds protocolError, as defined in [Section 4.14.1 in RFC4511](https://datatracker.ietf.org/doc/html/rfc4511#section-4.14.1),
25+
then Authgear will treat it as no TLS is required.
26+
27+
When the connection URL starts with `ldaps://`, then Authgear will connect to the LDAP server with TLS directly, without using StartTLS.
28+
29+
## Authenticate Authgear as a LDAP client
30+
31+
It is very common that before a LDAP client can run any [Search Operation](https://datatracker.ietf.org/doc/html/rfc4511#section-4.5),
32+
the LDAP client must perform [Bind Operation](https://datatracker.ietf.org/doc/html/rfc4511#section-4.2) first.
33+
34+
Authgear supports Simple Bind with username and password.
35+
36+
## Configuration of LDAP servers
37+
38+
The following example demonstrates how the developer should configure Authgear to connect to their LDAP servers.
39+
40+
In `authgear.yaml`
41+
42+
```yaml
43+
identity:
44+
ldap:
45+
servers:
46+
- name: ldap1
47+
url: "ldap://localhost:389"
48+
base_distinguished_name: "dc=localhost"
49+
relative_distinguished_name_attribute: "uid"
50+
- name: ldap2
51+
url: "ldap://mycompany.com:389"
52+
base_distinguished_name: "dc=mycompany,dc=com"
53+
relative_distinguished_name_attribute: "uid"
54+
```
55+
56+
- `identity.ldap.servers.name`: A name that only exists in `authgear.yaml` and `authgear.secrets.yaml` for associating a LDAP server. It serves no other purpose.
57+
- `identity.ldap.servers.url`: The connection URL to the LDAP server. The scheme MUST be `ldap:` or `ldaps:`. The URL MUST contain `host`, and optionally a port. If the port is omitted, the default port of the scheme is assumed. The default port of `ldap:` is `389`, while the default port of `ldaps:` is `636`. The URL MUST NOT contain other elements, such as path, nor query.
58+
59+
> Why does `identity.ldap.servers.url` allow scheme, host, and port?
60+
> The LDAP URL, defined in [Section 2 in RFC 4516](https://datatracker.ietf.org/doc/html/rfc4516#section-2), is syntactically different from the URL defined in [RFC3986](https://datatracker.ietf.org/doc/html/rfc3986).
61+
> In particular, a LDAP URL can contain multiple question mark characters.
62+
> To ease implementation, we do not support the LDAP URL, and require a RFC3986 URL (which is implemented by the standard library net/url package).
63+
64+
- `identity.ldap.servers.base_distinguished_name`: The base distinguished name to construct a Search Request, as defined in [Section 4.5.1 in RFC4511](https://datatracker.ietf.org/doc/html/rfc4511#section-4.5.1).
65+
- `identity.ldap.servers.relative_distinguished_name_attribute`: The attribute name Authgear should use to construct the search request. For example, if the value is `uid`, and the end-user gives a username of `user1`, and the base distinguished name is `dc=example,dc=com`, then the relative distinguished name is `uid=user1`, and the distinguished name is `uid=user1,dc=example,dc=com`.
66+
67+
> base_distinguished_name and relative_distinguished_name_attribute may not be sufficient if the developer needs to determine the DN in a more dynamic way.
68+
> In the future, we can support a new configuration, distinguished_name_template, which is a Go template that MUST return a DN.
69+
> It looks like
70+
> {{- if (hasSuffix $.Username "@mycompany.com") }}
71+
> {{- (ldapDN (ldapAttribute "uid" $.Username) (ldapParse "dc=mycompany,dc=com") ) }}
72+
> {{- else }}
73+
> {{- (ldapDN (ldapAttribute "uid" $.Username) (ldapParse "dc=anothercompany,dc=com") ) }}
74+
> {{- end }}
75+
76+
> TODO: The current configuration is missing an important feature. The feature is allow the developer to specify what attributes they want to retrieve from the LDAP server, and
77+
> what attributes map to which standard attributes.
78+
79+
In `authgear.secrets.yaml`
80+
81+
```yaml
82+
secrets:
83+
- data:
84+
items:
85+
- name: ldap1
86+
username: authgear
87+
password: secret1
88+
- name: ldap2
89+
username: authgear
90+
password: secret2
91+
key: ldap
92+
```
93+
94+
- `items.name`: To associate a LDAP server in `authgear.yaml`.
95+
- `items.username`: Optional. The username Authgear uses to authenticate itself to the LDAP server. If it is not provided, then Authgear does not authenticates itself, and assumes the LDAP server allows anonymous requests.
96+
- `items.password`: Optional. The password Authgear uses to authenticate itself to the LDAP server. If `username` is provided, then `password` is required.
97+
98+
## The database schema of a LDAP identity
99+
100+
```sql
101+
CREATE TABLE _auth_identity_ldap
102+
(
103+
id text PRIMARY KEY REFERENCES _auth_identity (id),
104+
app_id text NOT NULL,
105+
server_url text NOT NULL,
106+
distinguished_name text NOT NULL,
107+
claims jsonb NOT NULL,
108+
raw_entry_json jsonb NOT NULL
109+
);
110+
111+
CREATE UNIQUE INDEX _auth_identity_ldap_unique ON _auth_identity_ldap (app_id, server_url, distinguished_name);
112+
```
113+
114+
- `id`: The primary key of this table. This is the same as other `_auth_identity_*` tables.
115+
- `app_id`: The app ID of this table for multi-tenant. This is the same as other `_auth_identity_*` tables.
116+
- `server_url`: The URL to the LDAP server when this identity was created. The value is taken from the configuration at that moment. It does not change even if the URL in `authgear.yaml` changes.
117+
- `distinguished_name`: The distinguished name of this LDAP entry.
118+
- `claims`: The standard claims extracted from this LDAP entry.
119+
- `raw_entry_json`: The raw LDAP entry encoded in JSON. It looks like `{ "dn": "uid=johndoe,dc=example,dc=com", "attr1": ["value1"] }`.
120+
121+
## Handling of a LDAP identity
122+
123+
- To look up a LDAP identity in Authgear, we use the tuple `(app_id, server_url, distinguished_name)`.
124+
- Similar to OAuth identity, we update an LDAP identity when it is used in login.
125+
126+
## The UX of LDAP in Auth UI
127+
128+
In the MVP phase (that is, now), sign in with LDAP is like sign in with an OAuth provider.
129+
Except that the enter-username-and-password page is hosted by Authgear as a integral part of Auth UI.
130+
131+
In the future, we may consider an option to make LDAP "replaces" Login ID in the UX.

docs/specs/user-model.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
* [Identity](#identity)
44
+ [Identity Claims](#identity-claims)
55
+ [OAuth Identity](#oauth-identity)
6+
+ [LDAP Identity](#ldap-identity)
67
+ [WebAuthn Identity](#webauthn-identity)
78
+ [Anonymous Identity](#anonymous-identity)
89
- [Anonymous Identity JWT](#anonymous-identity-jwt)
@@ -38,8 +39,7 @@
3839
- [WebAuthn Authenticator](#webauthn-authenticator)
3940
- [TOTP Authenticator](#totp-authenticator)
4041
- [OOB-OTP Authenticator](#oob-otp-authenticator)
41-
- [Login Link](#login-link)
42-
+ [Skip verification](#skip-verification)
42+
* [Login Link](#login-link)
4343
+ [Device Token](#device-token)
4444
+ [Recovery Code](#recovery-code)
4545
* [Deleting a user](#deleting-a-user)
@@ -50,6 +50,7 @@
5050
+ [Deactivated user](#deactivated-user)
5151
+ [Scheduled account deletion or anonymization](#scheduled-account-deletion-or-anonymization)
5252
+ [Sessions](#sessions)
53+
+ [Configuration](#configuration)
5354

5455
# User Model
5556

@@ -61,18 +62,19 @@ A user has many identities. A user has many authenticators.
6162

6263
An identity is used to look up a user.
6364

64-
5 types of identity are supported.
65+
6 types of identity are supported.
6566

6667
- Login ID
6768
- OAuth
69+
- LDAP
6870
- WebAuthn
6971
- Anonymous
7072
- Biometric
7173

7274
A user either has no anonymous identity, or have exactly one anonymous identity.
7375
A user with anonymous identity is considered as anonymous user.
7476

75-
A user must have at least 1 Login ID identity or 1 OAuth identity.
77+
A user must have at least 1 Login ID identity, or at least 1 OAuth identity, or at least 1 LDAP identity.
7678

7779
### Identity Claims
7880

@@ -84,9 +86,19 @@ The claims are used to detect duplicate identity. For example, an Email Login ID
8486

8587
### OAuth Identity
8688

87-
OAuth identity is external identity from supported OAuth 2 IdPs. Only authorization code flow is supported. If the provider supports OIDC, OIDC is preferred over provider-specific OAuth 2 protocol.
89+
OAuth identity is an external identity from supported OAuth 2 IdPs. Only authorization code flow is supported. If the provider supports OIDC, OIDC is preferred over provider-specific OAuth 2 protocol.
8890

89-
OAuth identity does not require primary authentication.
91+
An OAuth identity does not require primary authentication, nor secondary authentication.
92+
93+
### LDAP Identity
94+
95+
LDAP identity is an external identity from a LDAPv3 server.
96+
A LDAP identity is internally identified in Authgear with the URL to the LDAP server, and the DN of the entry.
97+
98+
A LDAP identity does not require primary authentication, the LDAP server is responsible for authenticating with the Bind operation.
99+
If the project has configured secondary authentication, then a LDAP identity requires secondary authentication.
100+
101+
For the details of LDAP, please see [./ldap.md](./ldap.md)
90102

91103
### WebAuthn Identity
92104

0 commit comments

Comments
 (0)