mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-07 23:03:00 -06:00
0484949b80
External IdP objects represent OAuth 2.0 clients that can be used to perform OAuth 2.0 device authorization grant flow. Related: https://pagure.io/freeipa/issue/8805 Related: https://pagure.io/freeipa/issue/8804 Related: https://pagure.io/freeipa/issue/8803 Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com> Signed-off-by: Florence Blanc-Renaud <flo@redhat.com> Reviewed-By: Francisco Trivino <ftrivino@redhat.com> Reviewed-By: Sumit Bose <sbose@redhat.com>
404 lines
21 KiB
Markdown
404 lines
21 KiB
Markdown
# IPA and an external identity provider integration - idp objects
|
|
|
|
IPA needs to store and manage IdP references. As a new IPA object, IdP reference needs:
|
|
* creation of an LDAP object class and LDAP attribute types used to store IdP
|
|
information
|
|
* creation of a new IPA API managing the IdP references
|
|
* definition of access control lists protecting access to the IdP references
|
|
* upgrade and backward compatibility.
|
|
|
|
## LDAP Schema
|
|
|
|
### New attribute types
|
|
|
|
The external IdP reference object needs to contain the following information:
|
|
* name (string)
|
|
* authorization endpoint (URI mapped as a case-sensitive string),
|
|
* device authorization endpoint (URI mapped as a case-sensitive string), for instance https://oauth2.googleapis.com/device/code
|
|
* token endpoint (URI mapped as a case-sensitive string), for instance https://oauth2.googleapis.com/token
|
|
* userinfo endpoint (URI mapped as a case-sensitive string)
|
|
* JWKS endpoint (URI mapped as a case-sensitive string)
|
|
* OIDC URL (URI mapped as a case-sensitive string)
|
|
* `client_id` (case-sensitive string)
|
|
* `client_secret` (optional, may be an empty string, must be protected as a password)
|
|
* scope (case sensitive string)
|
|
* user subject attribute (case-sensitive string)
|
|
|
|
Additionally, an idp object needs to be referenced from a user object, thus a link attribute has to be defined. This translates into the following attribute type definitions:
|
|
```text
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.15 NAME 'ipaIdpDevAuthEndpoint' DESC 'Identity Provider Device Authorization Endpoint' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.9' )
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.16 NAME 'ipaIdpAuthEndpoint' DESC 'Identity Provider Authorization Endpoint' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.9' )
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.17 NAME 'ipaIdpTokenEndpoint' DESC 'Identity Provider Token Endpoint' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.9' )
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.18 NAME 'ipaIdpClientId' DESC 'Identity Provider Client Identifier' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.9' )
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.19 NAME 'ipaIdpClientSecret' DESC 'Identity Provider Client Secret' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'IPA v4.9' )
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.20 NAME 'ipaIdpScope' DESC 'Identity Provider Scope' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.9' )
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.21 NAME 'ipaIdpConfigLink' DESC 'Corresponding Identity Provider Configuration link' SUP distinguishedName EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN 'IPA v4.9')
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.22 NAME 'ipaIdpSub' DESC 'Identity Provider User Subject' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.9' )
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.23 NAME 'ipaIdpIssuerURL' DESC 'Identity Provider OIDC URL' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.9' )
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.24 NAME 'ipaIdpUserInfoEndpoint' DESC 'Identity Provider UserInfo Endpoint' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.9' )
|
|
attributeTypes: (2.16.840.1.113730.3.8.23.25 NAME 'ipaIdpKeysEndpoint' DESC 'Identity Provider JWKS Endpoint' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.9' )
|
|
```
|
|
|
|
### New objectclasses for idp
|
|
|
|
In addition to IdP configuration object, a special object class is added to be able to store IdP configuration link in a user object:
|
|
|
|
```text
|
|
objectClasses: (2.16.840.1.113730.3.8.24.6 NAME 'ipaIdP' SUP top STRUCTURAL DESC 'Identity Provider Configuration' MUST ( cn ) MAY ( ipaIdpDevAuthEndpoint $ ipaIdpAuthEndpoint $ ipaIdpTokenEndpoint $ ipaIdpUserInfoEndpoint $ ipaIdpKeysEndpoint $ ipaIdpClientId $ description $ ipaIdpClientSecret $ ipaIdpScope $ ipaIdpIssuerURL $ ipaIdpSub ) X-ORIGIN 'IPA v4.9' )
|
|
objectClasses: (2.16.840.1.113730.3.8.24.7 NAME 'ipaIdpUser' SUP top AUXILIARY DESC 'User from an external Identity Provider ' MAY ( ipaIdpConfigLink $ ipaIdpSub ) X-ORIGIN 'IPA v4.9' )
|
|
```
|
|
|
|
In the current implementation, a user can be connected to a single IdP
|
|
reference. This is similar to RADIUS proxy references already used in IPA. For
|
|
both RADIUS and IdP cases IPA needs to know a particular user identity within
|
|
the context of the referred resource (IdP or RADIUS server). Without associated
|
|
IdP user information authentication of the IPA user will not be triggered.
|
|
|
|
### LDAP indices
|
|
|
|
Indices need to be defined for all the attributes that can be used in
|
|
`ipa idp-find` command.
|
|
|
|
- The `cn` attribute is already indexed.
|
|
- The `ipaidpdevauthendpoint`, `ipaidpauthendpoint`, `idaidptokenendpoint`, and `idpidpscope` attributes
|
|
need indices (equality and substring indices).
|
|
|
|
|
|
## IdP reference API
|
|
|
|
### Command Line Interface
|
|
```
|
|
idp-add Add new external IdP server.
|
|
idp-del Delete an external IdP server.
|
|
idp-find Search for external IdP servers.
|
|
idp-mod Modify an external IdP server.
|
|
idp-show Display information about an external IdP server.
|
|
```
|
|
|
|
Following common options defined for the external IdP server object:
|
|
|
|
| Option | Description |
|
|
|:--------------------- | :--------------------- |
|
|
| `--dev-auth-uri`=URI | Device authorization endpoint |
|
|
| `--auth-uri`=URI | General OAuth 2.0 authorization endpoint |
|
|
| `--token-uri`=URI | Token endpoint |
|
|
| `--userinfo-uri`=URI | User information endpoint |
|
|
| `--keys-uri`=URI | JWKS endpoint |
|
|
| `--issuer-url`=URI | The Identity Provider OIDC URL |
|
|
| `--client-id`=STR | The client identifier issued by the IdP during registration |
|
|
| `--secret` | The client secret, asked interactively |
|
|
| `--scope`=STR | OAuth 2.0 scopes of the access request |
|
|
| `--idp-user-id`=STR | Attribute holding user identity in User info |
|
|
|
|
`--scope` option expects space-separated list of OAuth 2.0 scopes. Since the
|
|
value includes space, it has to be enclosed in quotes:
|
|
|
|
```
|
|
ipa idp-mod foo --scope="openid email"
|
|
```
|
|
|
|
Not all common options are required at the same time in the context of different commands.
|
|
|
|
`ipa idp-add` command adds three more options:
|
|
|
|
| Option | Description |
|
|
|:--------------------- | :--------------------- |
|
|
| `--provider`=STR | One of [google, github, microsoft, okta, keycloak] |
|
|
| `--org`=STR | IdP-specific organization (tenant or realm) ID |
|
|
| `--base-url`=URI | IdP-specific base URL (e.g. `idp-host.$DOMAIN:$PORT/prefix`)|
|
|
|
|
In order to ease the creation of idp, IPA pre-populates a set of
|
|
templates for well-known IdPs so that the user does not need to provide the
|
|
individual endpoint details.
|
|
|
|
For instance, for `google` provider, adding a new IdP would look like:
|
|
```
|
|
ipa idp-add MyGoogleIdP --provider google \
|
|
--client-id nZ8JDrV8Hklf3JumewRl2ke3ovPZn5Ho \
|
|
```
|
|
|
|
This call would be equivalent to the following:
|
|
```
|
|
ipa idp-add MyGoogleIdP \
|
|
--dev-auth-uri https://oauth2.googleapis.com/device/code \
|
|
--token-uri https://oauth2.googleapis.com/token \
|
|
--client-id nZ8JDrV8Hklf3JumewRl2ke3ovPZn5Ho \
|
|
--scope "profile email"
|
|
```
|
|
|
|
As a consequence, `--provider` option and URI-specific options `--auth-uri`,
|
|
`--dev-auth-uri`, `--keys-uri`, and `--token-uri` are mutually exclusive.
|
|
|
|
`ipa idp-add` command does not currently use `--issuer-url` option URL
|
|
to dynamically look up other URIs through a well-known OIDC endpoints. Some of
|
|
the IdPs do not provide well-known OIDC endpoints URL at all. This option can
|
|
be used in `ipa idp-mod` to set `ipaIdpIssuerURL` LDAP attribute. If this
|
|
attribute is set, it will be passed to SSSD's `oidc_child` helper during actual
|
|
authorization processing.
|
|
|
|
The `scope` is optional, and the `client_id` is mandatory.
|
|
|
|
The `client_secret` must not be displayed by `ipa idp-find` or `ipa idp-show`
|
|
commands unless permission 'System: Read External IdP client secret' is
|
|
assigned to the authenticated user running the command.
|
|
|
|
### Pre-populated IdP templates
|
|
|
|
List of pre-populated IdP types is currently limited by the following provider
|
|
"names":
|
|
|
|
* google
|
|
* github
|
|
* microsoft
|
|
* okta
|
|
* keycloak
|
|
|
|
Some IdP providers support parametrized URIs which include organization or a
|
|
realm name, or specific base URL, or both.
|
|
|
|
One notable omission in the pre-populated IdP types above is Gitlab.
|
|
|
|
FreeIPA only supports IdPs that implement OAuth 2.0 Device authorization
|
|
grant flow as defined by the [RFC 8628](https://www.rfc-editor.org/rfc/rfc8628).
|
|
If required IdP cannot be made to support Device authorization grant flow, it
|
|
is recommended to use OAuth 2.0 federation within an IdP that supports this
|
|
method. Gitlab does not support OAuth 2.0 Device authorization grant flow and
|
|
thus is not supported directly.
|
|
|
|
SSSD 2.7.0 implements Kerberos pre-authentication method `idp` (registered as a
|
|
pre-authentication type 152, PA-REDHAT-IDP-OAUTH2). It relies on IPA KDB driver
|
|
to provide a metadata in the Kerberos principal entry to specify use of IdP.
|
|
KDC side of the pre-authentication method 'idp' then communicates with IPA
|
|
`ipa-otpd` daemon which reads external IdP reference object associated with the
|
|
user and spawns `oidc_child` helper from SSSD to complete authorization.
|
|
|
|
`oidc_child` helper performs OAuth 2.0 device authorization grant flow. Since
|
|
it only supports a single authorization method, not all endpoints are required.
|
|
In order to authenticate users associated with external IdPs to access IPA Web
|
|
UI, a general OAuth 2.0 authorization endpoint is required. Thus, this endpoint
|
|
information is stored in the object but not otherwise used by the Kerberos
|
|
flow.
|
|
|
|
#### Google IdPs
|
|
|
|
Choosing `--provider=google` would expand to use the following options:
|
|
|
|
| Option | Value |
|
|
|:--------------------- | :--------------------- |
|
|
| `--auth-uri`=URI | `https://accounts.google.com/o/oauth2/auth` |
|
|
| `--dev-auth-uri`=URI | `https://oauth2.googleapis.com/device/code` |
|
|
| `--token-uri`=URI | `https://oauth2.googleapis.com/token` |
|
|
| `--userinfo-uri`=URI | `https://openidconnect.googleapis.com/v1/userinfo` |
|
|
| `--keys-uri`=URI | `https://www.googleapis.com/oauth2/v3/certs` |
|
|
| `--scope`=STR | `openid email` |
|
|
| `--idp-user-id`=STR | `email` |
|
|
|
|
#### Github IdPs
|
|
|
|
Choosing `--provider=github` would expand to use the following options:
|
|
|
|
| Option | Value |
|
|
|:--------------------- | :--------------------- |
|
|
| `--auth-uri`=URI | `https://github.com/login/oauth/authorize` |
|
|
| `--dev-auth-uri`=URI | `https://github.com/login/device/code` |
|
|
| `--token-uri`=URI | `https://github.com/login/oauth/access_token` |
|
|
| `--userinfo-uri`=URI | `https://openidconnect.googleapis.com/v1/userinfo` |
|
|
| `--keys-uri`=URI | `https://api.github.com/user` |
|
|
| `--scope`=STR | `user` |
|
|
| `--idp-user-id`=STR | `login` |
|
|
|
|
Please note that Github explicitly states that a user login is not unique and
|
|
can be reused after a user account was deleted. The configuration above aims
|
|
for an easy setup for testing. If production deployment with Github IdP would
|
|
be required, it is recommended to change `--idp-user-id` to a more unique subject
|
|
like `id`. Unfortunately, Github UI does not give an easy way to discover a
|
|
user ID. Other IdPs also lack an easy way to resolve these internal identifiers
|
|
when not authorized by the user themselves.
|
|
|
|
For Github, user's ID can be looked up without authentication through the Users
|
|
API. Assuming we have `curl` and `jq` utilities available, a request to
|
|
discover an ID of a Github user named `test` would look like:
|
|
|
|
```
|
|
$ curl --silent \
|
|
-H "Accept: application/vnd.github.v3+json" \
|
|
https://api.github.com/users/test | jq .id
|
|
|
|
383316
|
|
```
|
|
|
|
|
|
#### Microsoft IdPs
|
|
|
|
Microsoft Azure IdPs allow parametrization based on the Azure tenant ID
|
|
which can be specified with `--org` option to `ipa idp-add`. If support for
|
|
live.com IdP is required, specify organization ID `common`, e.g.
|
|
|
|
```
|
|
ipa idp-add LiveCom --provider microsoft --org common --client-id some-client-id
|
|
```
|
|
|
|
Choosing `--provider=microsoft` would expand to use the following options. A string
|
|
`${ipaidporg}` will be replaced by the value of `--org` option.
|
|
|
|
| Option | Value |
|
|
|:--------------------- | :--------------------- |
|
|
| `--auth-uri`=URI | `https://login.microsoftonline.com/${ipaidporg}/oauth2/v2.0/authorize` |
|
|
| `--dev-auth-uri`=URI | `https://login.microsoftonline.com/${ipaidporg}/oauth2/v2.0/devicecode`|
|
|
| `--token-uri`=URI | `https://login.microsoftonline.com/${ipaidporg}/oauth2/v2.0/token` |
|
|
| `--userinfo-uri`=URI | `https://graph.microsoft.com/oidc/userinfo` |
|
|
| `--keys-uri`=URI | `https://login.microsoftonline.com/common/discovery/v2.0/keys` |
|
|
| `--scope`=STR | `openid email` |
|
|
| `--idp-user-id`=STR | `email` |
|
|
|
|
|
|
#### Okta IdPs
|
|
|
|
Upon registration of a new organization in Okta, a new base URL is associated
|
|
with it. It can be specified with `--base-url` option to `ipa idp-add`:
|
|
|
|
```
|
|
ipa idp-add MyOkta --provider okta --base-url dev-12345.okta.com --client-id some-client-id
|
|
```
|
|
|
|
Choosing `--provider=okta` would expand to use the following options. A string
|
|
`${ipaidpbaseurl}` will be replaced by the value of `--base-url` option.
|
|
|
|
| Option | Value |
|
|
|:--------------------- | :--------------------- |
|
|
| `--auth-uri`=URI | `https://${ipaidpbaseurl}/oauth2/v1/authorize` |
|
|
| `--dev-auth-uri`=URI | `https://${ipaidpbaseurl}/oauth2/v1/device/authorize` |
|
|
| `--token-uri`=URI | `https://${ipaidpbaseurl}/oauth2/v1/token` |
|
|
| `--userinfo-uri`=URI | `https://${ipaidpbaseurl}/oauth2/v1/userinfo` |
|
|
| `--scope`=STR | `openid email` |
|
|
| `--idp-user-id`=STR | `email` |
|
|
|
|
|
|
#### Keycloak IdPs
|
|
|
|
Keycloak allows defining multiple realms (organizations). Since it is often a
|
|
part of a custom deployment, both base URL and realm ID are required and
|
|
can be specified with `--base-url` and `--org` options to `ipa idp-add`:
|
|
|
|
```
|
|
ipa idp-add MySSO --provider keycloak \
|
|
--org master --base-url keycloak.$DOMAIN:$PORT/prefix \
|
|
--client-id some-client-id
|
|
```
|
|
|
|
Quarkus version of the Keycloak 17 or later has removed `/auth/` part of the URI. If
|
|
your deployment is using non-Quarkus distribution of the Keycloak, `--base-url`
|
|
would need to include `/auth/` component as well.
|
|
|
|
Choosing `--provider=keycloak` would expand to use the following options. A string
|
|
`${ipaidpbaseurl}` will be replaced by the value of `--base-url` option. A string
|
|
`${ipaidporg}` will be replaced by the value of `--org` option.
|
|
|
|
| Option | Value |
|
|
|:--------------------- | :--------------------- |
|
|
| `--auth-uri`=URI | `https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/openid-connect/auth` |
|
|
| `--dev-auth-uri`=URI | `https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/openid-connect/auth/device` |
|
|
| `--token-uri`=URI | `https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/openid-connect/token` |
|
|
| `--userinfo-uri`=URI | `https://${ipaidpbaseurl}/realms/${ipaidporg}/protocol/openid-connect/userinfo` |
|
|
| `--scope`=STR | `openid email` |
|
|
| `--idp-user-id`=STR | `email` |
|
|
|
|
|
|
#### User-specific options
|
|
|
|
Similar to RADIUS proxy support, external IdP authentication only triggered
|
|
when the following three conditions are true:
|
|
|
|
* user entry contains external IdP reference
|
|
* user entry has external IdP user name (subject) set
|
|
* either global user authentication type or per-user authentication type includes `idp` method
|
|
|
|
Therefore, two additional attributes available for User object in IPA API:
|
|
|
|
| Option | Description |
|
|
|:--------------------- | :--------------------- |
|
|
| `--idp`=STR | External IdP object reference |
|
|
| `--idp-user-id`=STR | External IdP's user name (subject) |
|
|
|
|
External IdP user name format is specific to individual IdPs. Most OAuth 2.0
|
|
IdPs provide `email` scope to retrieve email associated with the OAuth 2.0
|
|
resource owner (user) subject. Pre-populated IdP templates described above
|
|
include `email` scope by default. If external IdP references were created with
|
|
the help of these templates, user's email can be set to `--idp-user-id` to match
|
|
the resource owner subject. Please read Security section for details on when
|
|
this is inappropriate.
|
|
|
|
Below is an example how to associate a user account with a specific IdP reference:
|
|
|
|
```
|
|
ipa user-mod test --user-auth-type=idp --idp google --idp-user-id test@example.test
|
|
```
|
|
|
|
### WebUI
|
|
|
|
The IdP server objects need to be accessible in the WebUI. A new tab for
|
|
IdP will be created in the "Authentication" section, similar to
|
|
the existing _RADIUS servers_ tab. It will contain a table with the
|
|
list of IdP objects, enabling to add or delete an object.
|
|
Each object must be clickable in order to be edited.
|
|
|
|
In the _settings_ page for IdP objects, all the properties must be editable
|
|
but the client secret must not be displayed unless the authenticated user permitted
|
|
with the help of the `System: Read External IdP client secret` permission.
|
|
|
|
In order to change the client secret, the admin will click on
|
|
_Actions:Reset secret_.
|
|
|
|
Web UI for user entry attributes have to be extended to include both external
|
|
IdP reference and IdP user subject.
|
|
|
|
## Access control
|
|
|
|
Specific permissions need to be created to protect access to the IdP objects:
|
|
* System: Add External IdP
|
|
* System: Read External IdP (does not provide access to the client secret)
|
|
* System: Modify External IdP
|
|
* System: Delete External IdP
|
|
* System: Read External IdP client secret
|
|
|
|
Specific Privileges:
|
|
* External IdP Administrator: all the permissions related to external idp
|
|
except _System: Read External IdP client secret_
|
|
|
|
Any user who is a member of the admins group will be able to manage the
|
|
IdP server objects.
|
|
|
|
## Security
|
|
|
|
IPA objects representing external IdP references are OAuth 2.0 clients. The
|
|
client ID and client secret details can be used to impersonate OAuth 2.0
|
|
client. For public OAuth 2.0 client access to Client ID is enough. It is not
|
|
considered secure as it is typically a part of the HTTP redirect in OAuth 2.0
|
|
authorization flows.
|
|
|
|
Client secret has to be protected as it is typically used for private (trusted)
|
|
OAuth 2.0 clients.
|
|
|
|
Kerberos pre-authentication method `idp` relies on the user subject
|
|
(`ipaIdpSub` LDAP attribute) defined in the LDAP object entry to match the
|
|
Kerberos principal and the OAuth 2.0 resource owner. When `openid` OAuth 2.0
|
|
scope is used, this typically maps to `sub` value. Since there are no ways to
|
|
pull this value for all users in advance, pre-populated IdP templates set OAuth
|
|
2.0 scopes to include `email` and then use `email` to map IdP subject where possible.
|
|
There are some well-known IdPs which allow reuse of user accounts and emails, this
|
|
applies to both Github and Gitlab. Since Gitlab does not support OAuth 2.0
|
|
Device authorization grant flow, it is not an issue in itself for this project. However,
|
|
for Github it is known that user accounts can be recycled after their removal. In
|
|
this case we would recommend to use internal Github identifier instead.
|
|
|
|
## Upgrade and backward compatibility
|
|
|
|
As the new object class and attribute types are added in the LDAP schema, there
|
|
is no need for a specific upgrade task.
|
|
Upon normal upgrade, the new schema is applied and replicated to the other
|
|
servers, even if they are installed with a version which does not define the
|
|
new object class / attribute types.
|
|
|
|
In a mixed topology containing old and new servers, the new API is provided
|
|
only by the new servers.
|