[[how-to-jpa]]
= How-to: Implement core services with JPA
:index-link: ../how-to.html
:docs-dir: ..

This guide shows how to implement the xref:{docs-dir}/core-model-components.adoc#core-model-components[core services] of xref:{docs-dir}/index.adoc#top[Spring Authorization Server] with JPA.
The purpose of this guide is to provide a starting point for implementing these services yourself, with the intention that you can make modifications to suit your needs.

* <<define-data-model>>
* <<create-jpa-entities>>
* <<create-spring-data-repositories>>
* <<implement-core-services>>

[[define-data-model]]
== Define the data model

This guide provides a starting point for the data model and uses the simplest possible structure and data types.
To come up with the initial schema, we begin by reviewing the xref:{docs-dir}/core-model-components.adoc#core-model-components[domain objects] used by the core services.

[NOTE]
Except for token, state, metadata, settings, and claims values, we use the JPA default column length of 255 for all columns.
In reality, the length and even type of columns you use may need to be customized.
You are encouraged to experiment and test before deploying to production.

* <<client-schema>>
* <<authorization-schema>>
* <<authorization-consent-schema>>

[[client-schema]]
=== Client Schema

The xref:{docs-dir}/core-model-components.adoc#registered-client[`RegisteredClient`] domain object contains a few multi-valued fields and some settings fields that require storing arbitrary key/value data.
The following listing shows the `client` schema.

.Client Schema
[source,sql]
----
CREATE TABLE client (
    id varchar(255) NOT NULL,
    clientId varchar(255) NOT NULL,
    clientIdIssuedAt timestamp DEFAULT CURRENT_TIMESTAMP NOT NULL,
    clientSecret varchar(255) DEFAULT NULL,
    clientSecretExpiresAt timestamp DEFAULT NULL,
    clientName varchar(255) NOT NULL,
    clientAuthenticationMethods varchar(1000) NOT NULL,
    authorizationGrantTypes varchar(1000) NOT NULL,
    redirectUris varchar(1000) DEFAULT NULL,
    postLogoutRedirectUris varchar(1000) DEFAULT NULL,
    scopes varchar(1000) NOT NULL,
    clientSettings varchar(2000) NOT NULL,
    tokenSettings varchar(2000) NOT NULL,
    PRIMARY KEY (id)
);
----

[[authorization-schema]]
=== Authorization Schema

The xref:{docs-dir}/core-model-components.adoc#oauth2-authorization[`OAuth2Authorization`] domain object is more complex and contains several multi-valued fields as well as numerous arbitrarily long token values, metadata, settings and claims values.
The built-in JDBC implementation utilizes a flattened structure that prefers performance over normalization, which we adopt here as well.

[CAUTION]
It has been difficult to find a flattened database schema that works well in all cases and with all database vendors.
You may need to normalize or heavily alter the following schema for your needs.

The following listing shows the `authorization` schema.

.Authorization Schema
[source,sql]
----
CREATE TABLE authorization (
    id varchar(255) NOT NULL,
    registeredClientId varchar(255) NOT NULL,
    principalName varchar(255) NOT NULL,
    authorizationGrantType varchar(255) NOT NULL,
    authorizedScopes varchar(1000) DEFAULT NULL,
    attributes varchar(4000) DEFAULT NULL,
    state varchar(500) DEFAULT NULL,
    authorizationCodeValue varchar(4000) DEFAULT NULL,
    authorizationCodeIssuedAt timestamp DEFAULT NULL,
    authorizationCodeExpiresAt timestamp DEFAULT NULL,
    authorizationCodeMetadata varchar(2000) DEFAULT NULL,
    accessTokenValue varchar(4000) DEFAULT NULL,
    accessTokenIssuedAt timestamp DEFAULT NULL,
    accessTokenExpiresAt timestamp DEFAULT NULL,
    accessTokenMetadata varchar(2000) DEFAULT NULL,
    accessTokenType varchar(255) DEFAULT NULL,
    accessTokenScopes varchar(1000) DEFAULT NULL,
    refreshTokenValue varchar(4000) DEFAULT NULL,
    refreshTokenIssuedAt timestamp DEFAULT NULL,
    refreshTokenExpiresAt timestamp DEFAULT NULL,
    refreshTokenMetadata varchar(2000) DEFAULT NULL,
    oidcIdTokenValue varchar(4000) DEFAULT NULL,
    oidcIdTokenIssuedAt timestamp DEFAULT NULL,
    oidcIdTokenExpiresAt timestamp DEFAULT NULL,
    oidcIdTokenMetadata varchar(2000) DEFAULT NULL,
    oidcIdTokenClaims varchar(2000) DEFAULT NULL,
    userCodeValue varchar(4000) DEFAULT NULL,
    userCodeIssuedAt timestamp DEFAULT NULL,
    userCodeExpiresAt timestamp DEFAULT NULL,
    userCodeMetadata varchar(2000) DEFAULT NULL,
    deviceCodeValue varchar(4000) DEFAULT NULL,
    deviceCodeIssuedAt timestamp DEFAULT NULL,
    deviceCodeExpiresAt timestamp DEFAULT NULL,
    deviceCodeMetadata varchar(2000) DEFAULT NULL,
    PRIMARY KEY (id)
);
----

[[authorization-consent-schema]]
=== Authorization Consent Schema

The xref:{docs-dir}/core-model-components.adoc#oauth2-authorization-consent[`OAuth2AuthorizationConsent`] domain object is the simplest to model and contains only a single multi-valued field in addition to a composite key.
The following listing shows the `authorizationConsent` schema.

.Authorization Consent Schema
[source,sql]
----
CREATE TABLE authorizationConsent (
    registeredClientId varchar(255) NOT NULL,
    principalName varchar(255) NOT NULL,
    authorities varchar(1000) NOT NULL,
    PRIMARY KEY (registeredClientId, principalName)
);
----

[[create-jpa-entities]]
== Create JPA entities

The preceding schema examples provide a reference for the structure of the entities we need to create.

[NOTE]
The following entities are minimally annotated and are just examples.
They allow the schema to be created dynamically and therefore do not require the above sql scripts to be executed manually.

* <<client-entity>>
* <<authorization-entity>>
* <<authorization-consent-entity>>

[[client-entity]]
=== Client Entity

The following listing shows the `Client` entity, which is used to persist information mapped from the xref:{docs-dir}/core-model-components.adoc#registered-client[`RegisteredClient`] domain object.

[[sample.jpa.entity.client]]
.Client Entity
include::code:Client[]

[[authorization-entity]]
=== Authorization Entity

The following listing shows the `Authorization` entity, which is used to persist information mapped from the xref:{docs-dir}/core-model-components.adoc#oauth2-authorization[`OAuth2Authorization`] domain object.

[[sample.jpa.entity.authorization]]
.Authorization Entity
include::code:Authorization[]

[[authorization-consent-entity]]
=== Authorization Consent Entity

The following listing shows the `AuthorizationConsent` entity, which is used to persist information mapped from the xref:{docs-dir}/core-model-components.adoc#oauth2-authorization-consent[`OAuth2AuthorizationConsent`] domain object.

[[sample.jpa.entity.authorizationConsent]]
.Authorization Consent Entity
include::code:AuthorizationConsent[]

[[create-spring-data-repositories]]
== Create Spring Data repositories

By closely examining the interfaces of each core service and reviewing the `Jdbc` implementations, we can derive a minimal set of queries needed for supporting a JPA version of each interface.

* <<client-repository>>
* <<authorization-repository>>
* <<authorization-consent-repository>>

[[client-repository]]
=== Client Repository

The following listing shows the `ClientRepository`, which is able to find a <<client-entity,`Client`>> by the `id` and `clientId` fields.

[[sample.jpa.repository.client]]
.Client Repository
include::code:ClientRepository[]

[[authorization-repository]]
=== Authorization Repository

The following listing shows the `AuthorizationRepository`, which is able to find an <<authorization-entity,`Authorization`>> by the `id` field as well as the `state`, `authorizationCodeValue`, `accessTokenValue`, `refreshTokenValue`, `userCodeValue` and `deviceCodeValue` token fields.
It also allows querying a combination of token fields.

[[sample.jpa.repository.authorization]]
.Authorization Repository
include::code:AuthorizationRepository[]

[[authorization-consent-repository]]
=== Authorization Consent Repository

The following listing shows the `AuthorizationConsentRepository`, which is able to find and delete an <<authorization-consent-entity,`AuthorizationConsent`>> by the `registeredClientId` and `principalName` fields that form a composite primary key.

[[sample.jpa.repository.authorizationConsent]]
.Authorization Consent Repository
include::code:AuthorizationConsentRepository[]

[[implement-core-services]]
== Implement core services

With the above <<create-jpa-entities,entities>> and <<create-spring-data-repositories,repositories>>, we can begin implementing the core services.
By reviewing the `Jdbc` implementations, we can derive a minimal set of internal utilities for converting to and from string values for enumerations and reading and writing JSON data for attributes, settings, metadata and claims fields.

[CAUTION]
Keep in mind that writing JSON data to text columns with a fixed length has proven problematic with the `Jdbc` implementations.
While these examples continue to do so, you may need to split these fields out into a separate table or data store that supports arbitrarily long data values.

* <<registered-client-repository>>
* <<authorization-service>>
* <<authorization-consent-service>>

[[registered-client-repository]]
=== Registered Client Repository

The following listing shows the `JpaRegisteredClientRepository`, which uses a <<client-repository,`ClientRepository`>> for persisting a <<client-entity,`Client`>> and maps to and from the xref:{docs-dir}/core-model-components.adoc#registered-client[`RegisteredClient`] domain object.

[[sample.jpa.service.client]]
.`RegisteredClientRepository` Implementation
include::code:JpaRegisteredClientRepository[]

[[authorization-service]]
=== Authorization Service

The following listing shows the `JpaOAuth2AuthorizationService`, which uses an <<authorization-repository,`AuthorizationRepository`>> for persisting an <<authorization-entity,`Authorization`>> and maps to and from the xref:{docs-dir}/core-model-components.adoc#oauth2-authorization[`OAuth2Authorization`] domain object.

[[sample.jpa.service.authorization]]
.`OAuth2AuthorizationService` Implementation
include::code:JpaOAuth2AuthorizationService[]

[[authorization-consent-service]]
=== Authorization Consent Service

The following listing shows the `JpaOAuth2AuthorizationConsentService`, which uses an <<authorization-consent-repository,`AuthorizationConsentRepository`>> for persisting an <<authorization-consent-entity,`AuthorizationConsent`>> and maps to and from the xref:{docs-dir}/core-model-components.adoc#oauth2-authorization-consent[`OAuth2AuthorizationConsent`] domain object.

[[sample.jpa.service.authorizationConsent]]
.`OAuth2AuthorizationConsentService` Implementation
include::code:JpaOAuth2AuthorizationConsentService[]
