@@ -9,6 +9,232 @@ prev=security-api002.html
99[[overview-of-the-identity-store-interfaces]]
1010Overview of the Identity Store Interfaces
1111-----------------------------------------
12- Include a discussion about IdentityStore and IdentityStoreHandler interfaces,
13- how to implement the default implementation, and how to create a custom identity
14- store if desired.
12+ The Identity Store Interfaces are described in the following sections:
13+
14+ * link:#the-identitystore-interface[The IdentityStore Interface]
15+ * link:#the-identitystorehandler-interface[The IdentityStoreHandler Interface]
16+ * link:#the-passwordhash-interface[The PasswordHash Interface]
17+ * link:#the-remembermeidentitystore-interface[The RememberMeIdentityStore Interface]
18+
19+ [[the-identitystore-interface]]
20+ The IdentityStore Interface
21+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
22+ The `IdentityStore` interface defines an SPI for interacting with identity stores,
23+ which are directories or databases containing user account information.
24+ An implementation of the `IdentityStore` interface can validate users' credentials,
25+ provide information about the groups they belong to, or both. Most often, an
26+ `IdentityStore` implementation will interact with an external identity store --
27+ an LDAP server, for example -- to perform the actual credential validation and
28+ group lookups, but an `IdentityStore` may also manage user account data itself.
29+
30+ There are two built-in implementations of `IdentityStore` : an LDAP identity store,
31+ and a Database identity store. These identity stores delegate to external stores
32+ that must already exist; the IdentityStore implementations do not provide or
33+ manage the external store. They are configured with the parameters necessary
34+ to communicate with an external store using the following annotations:
35+
36+ * `LdapIdentityStoreDefinition` -- configures an identity store with the parameters
37+ necessary to communicate with an external LDAP server, validate user credentials,
38+ and/or lookup user groups.
39+
40+ * `DatabaseIdentityStoreDefinition` -- configures an identity store with the
41+ parameters necessary to connect to an external database, validate user credentials,
42+ and/or lookup user groups.
43+
44+ link:security-api004.html#running-the-built-in-database-identity-store-example[Running the Built-In Database Identity Store Example]
45+ demonstrates usage of the built-in database identity store.
46+
47+ Multiple implementations of `IdentityStore` may be present; if so, they are invoked
48+ in priority order, based on each identity store's self-declared priority and
49+ type (validation, groups, or both). An implementation of `IdentityStore` must be a
50+ CDI bean to be recognized and deployed at runtime, and is assumed to be normal scoped.
51+ IdentityStores are primarily intended for use by implementations of
52+ `HttpAuthenticationMechanisms` , but this is not a requirement.
53+ They can be used by other types of authentication mechanisms as well, or by containers.
54+
55+ [[the-identitystorehandler-interface]]
56+ The IdentityStoreHandler Interface
57+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
58+
59+ Authentication mechanisms do not interact with `IdentityStore` directly; instead,
60+ they call an `IdentityStoreHandler` . An implementation of the `IdentityStoreHandler`
61+ interface provides a single method, `validate(Credential)` , which, when invoked,
62+ iterates over the available IdentityStores and returns an aggregated result.
63+ An `IdentityStoreHandler` must also be a CDI bean, and is assumed to be normal scoped.
64+ At runtime, an authentication mechanism injects the `IdentityStoreHandler` and
65+ invokes on it. The `IdentityStoreHandler` , in turn, looks up the available IdentityStores
66+ and invokes on them to determine the aggregate result.
67+
68+ There is a built-in `IdentityStoreHandler` that implements a standard algorithm
69+ defined by JSR-375. The JSR-375 specification provides a full description of
70+ the algorithm, but it can be roughly summarized as follows:
71+
72+ * Iterate over the available validating IdentityStores, in priority order,
73+ until the provided Credential is validated or there are no more IdentityStores.
74+
75+ * If the Credential was validated, iterate over the available group-providing
76+ IdentityStores, in priority order, aggregating the groups returned by each store.
77+
78+ * Return the validated caller and group information.
79+
80+ An application may also supply its own `IdentityStoreHandler` , which can use any
81+ desired algorithm to select and invoke on IdentityStores, and return an
82+ aggregated (or non-aggregated) result.
83+
84+ IdentityStore Interface Details:
85+
86+ The IdentityStore interface itself has four methods:
87+
88+ * `validate(Credential)` -- validate a Credential, and return the result of that
89+ validation.
90+
91+ * `getCallerGroups(CredentialValidationResult)` -- return the groups associated
92+ with the caller indicated by the supplied `CredentialValidationResult` , which
93+ represents the result of a previous, successful validation.
94+
95+ * `validationTypes()` -- returns a Set of validation types (one or more of
96+ `VALIDATE` , `PROVIDE_GROUPS` )
97+ that indicate the operations supported by this instance of the `IdentityStore` .
98+
99+ * `priority()` -- returns a positive integer representing the self-declared
100+ priority of this IdentityStore. Lower values represent higher priority.
101+
102+ Because `getCallerGroups()` is a sensitive operation -- it can return information
103+ about arbitrary users, and does not require that the caller provide the user's
104+ credential or proof of identity -- the caller should have the
105+ `IdentityStorePermission("getGroups")` permission. Enforcement of this check is
106+ incumbent on the implementation of the `getCallerGroups()` method; the built-in
107+ IdentityStores do check for this permission, if a SecurityManager is configured,
108+ and the built-in IdentityStoreHandler invokes the `getCallerGroups()` method in
109+ the context of a `PrivilegedAction` block.
110+
111+ [[the-passwordhash-interface]]
112+ The PasswordHash Interface
113+ ~~~~~~~~~~~~~~~~~~~~~~~~~~
114+
115+ Unlike some types of identity stores, for example LDAP directories,
116+ databases can store and retrieve user passwords, but can't verify them natively.
117+ Therefore, the built-in Database identity store must verify user passwords itself.
118+ Most often, this involves generating a hash of the user's password for comparison
119+ with a hash value stored in the database.
120+
121+ In order to provide maximum flexibility and interoperability, the Database identity
122+ store does not implement any specific password hashing algorithms. Instead, it
123+ defines the PasswordHash interface, and expects the application to provide an
124+ implementation of PasswordHash that can verify passwords from the specific store
125+ the application will use. The PasswordHash implementation must be made available
126+ as a dependent scoped bean, and is configured by providing the fully-qualified
127+ name of the desired type as a hashAlgorithm value on the DatabaseIdentityStoreDefinition.
128+
129+ [[the-remembermeidentitystore-interface]]
130+ The RememberMeIdentityStore Interface
131+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
132+ The `RememberMeIdentityStore` interface represents a special type of identity store.
133+ It is not directly related to the `IdentityStore` interface; that is, it does not
134+ implement or extend it. It does, however, perform a similar, albeit specialized, function.
135+
136+ In some cases, an application wants to "remember" a user's authenticated session
137+ for an extended period. For example, a web site may remember you when you visit,
138+ and prompt for your password only periodically, perhaps once every two weeks,
139+ as long as you don't explicitly log out.
140+
141+ RememberMe works as follows:
142+
143+ * When a request from an unauthenicated user is received, the user is authenticated
144+ using an `HttpAuthenticationMechanism` that is provided by the application
145+ (this is required -- `RememberMeIdentityStore` can only be used in conjunction with an
146+ application-supplied `HttpAuthenticationMechanism` ).
147+
148+ * After authentication, the configured `RememberMeIdentityStore` saves information
149+ about the user's authenticated identity, so that it be restored later, and
150+ generates a long-lived "remember me" login token that is sent back to the client,
151+ perhaps as a cookie.
152+
153+ * On a subsequent visit to the application, the client presents the login token.
154+ The `RememberMeIdentityStore` then validates the token and returns the stored user
155+ identity, which is then established as the user's authenticated identity.
156+ If the token is invalid or expired, it is discarded, the user is authenticated
157+ normally again, and a new login token is generated.
158+
159+ The `RememberMeIdentityStore` interface defines the following methods:
160+
161+ * `generateLoginToken(CallerPrincipal caller, Set<String> groups)` -- generate a
162+ login token for a newly authenticated user, and associate it with the provided
163+ caller/group information.
164+
165+ * `removeLoginToken(String token)` -- remove the (presumably expired or invalid)
166+ login token and any associated caller/group information.
167+
168+ * `validate(RememberMeCredential credential)` -- validate the supplied credential, and,
169+ if valid, return the associated caller/group information. (`RememberMeCredential` is
170+ essentially just a holder for a login token).
171+
172+ An implementation of `RememberMeIdentityStore` must be a CDI bean, and is assumed
173+ to be normal scoped. It is configured by adding a `RememberMe` annotation to an
174+ application's `HttpAuthenticationMechanism` , which indicates that a
175+ `RememberMeIdentityStore` is in use, and provides related configuration parameters.
176+ A container-supplied interceptor then intercepts calls to the `HttpAuthenticationMechanism` ,
177+ invokes the `RememberMeIdentityStore` as necessary before and after calls to the
178+ authentication mechanism, and ensures that the user's identity is correctly
179+ set for the session. The JSR-375 specification provides a detailed description
180+ of the required interceptor behavior.
181+
182+ Implementations of `RememberMeIdentityStore` should take care to manage tokens
183+ and user identity information securely. For example, login tokens should not
184+ contain sensitive user information, like credentials or sensitive attributes,
185+ to avoid exposing that information if an attacker were able to gain access
186+ to the token -- even an encrypted token is potentially vulnerable to an
187+ attacker with sufficient time/resources. Similarly, tokens should be
188+ encrypted/signed wherever possible, and sent only over secure channels (HTTPS).
189+ User identity information managed by a `RememberMeIdentityStore` should be stored
190+ as securely as possible (but does not necessarily need to be reliably persisted --
191+ the only impact of a "forgotten" session is that the user will be prompted to
192+ log in again).
193+
194+ The PasswordHash algorithm defines three methods:
195+
196+ * `initialize(Map<String,String> parameters)` -- initialize the PasswordHash with
197+ the supplied Map of parameters. The Database identity store calls this method when
198+ initializing, passing the `hashAlgorithmParameters` value of the
199+ `DatabaseIdentityStoreDefinition` annotation (after conversion to a Map).
200+
201+ * `verify(char[] password, String hashedPassword)` -- verify a caller-supplied
202+ password against the caller's stored password hash as retrieved from the database.
203+ The `hashedPassword` value should be provided exactly as it was returned from the database.
204+
205+ * generate(char[] password) -- generate a password hash from the supplied password.
206+ The value returned should be formatted and encoded exactly it would be stored
207+ in the database. While it is useful to generate the hash of a caller-supplied password
208+ during `verify()` , this method is intended primarily for use by applications or
209+ `IdentityStore` implementations that want to support password management/reset
210+ capability without having to duplicate the code used to verify passwords.
211+
212+ Note that, while the interface is oriented toward hashing passwords, it can also
213+ support alternative approaches, such as two-way encryption of stored passwords.
214+
215+ There is a built-in `Pbkdf2PasswordHash` implementation that supports, as it's
216+ name suggests, PBKDF2 password hashing. It supports several parameters that
217+ control the generation of hash values
218+ (key size, iterations, and so on -- see the Javadoc),
219+ and those parameters are encoded into the resulting hash value, so that hashes
220+ can be verified even if the currently configured parameters are different from
221+ the parameters in effect when a stored hash was generated.
222+
223+ While it is necessary to write a custom `PasswordHash` to enable interoperability
224+ with a legacy identity store that stores password hashes in a format other
225+ than the `Pbkdf2PasswordHash` format, developers should consider carefully
226+ whether `Pbkdf2PasswordHash` is sufficient for new identity stores, and avoid
227+ writing a new PasswordHash implementation without a solid understanding of the
228+ cryptographic and other security considerations involved. Some of the
229+ considerations specific to password hashing are:
230+
231+ * The requirements for hashing passwords differ considerably from the requirements
232+ for hashing in other contexts. In particular, speed is normally a virtue when
233+ generating hashes, but when generating password hashes, slower is better -- to
234+ slow down brute force attacks against hashed values.
235+
236+ * The comparison of a generated hash with a stored hash should take constant time,
237+ whether it succeeds or fails, in order to avoid giving an attacker clues about
238+ the password value based on the timing of failed attempts.
239+
240+ * A new random salt should be used each time a new password hash value is generated.
0 commit comments