Today I'm going to build a Spring Boot application
with Angular and Spring Boot to Authenticate a user with OAuth2 and Google. I've struggled
a lot to configure Google, because it doesn't Implement all the OAuth2 workflows. Google does
this to increase the security even more. One of the cases I couldn't configure is put all the
authentication in the frontend with the PKCE. So, I will implement the authorization code flow in a
stateless application and Angular as a frontend. When talking about OAuth2, many servers come in
place, the client server, the resources server and the authentication server. The client server
is the one which requests the user authentication. It's the one which wants to access a protected
resources. The resources server is the one which contains these protected resources. Sometimes,
the client and the resources server are the same server. That's the configuration I will use today.
And finally the authentication server is the server which has the authentication information.
It can be GitHub, Facebook, Keycloak or Google. And now, how does the authorization code flow
work? Let's say I go to a website, a website to chat complaining about my kids. Once the website
loads, I want to access my personal account, my private messages. But the website doesn't store
any credentials. All is delegated to Google. So it asks Google to authenticate the user. Google
returns me a URL which displays a login form. Once logged correctly, Google returns to the frontend
a code. The backend use this code to get an access token. This access token is the one which grants
me access to personal data. First I will use it to get the name of the authenticated user. Then
the frontend will use it to validate each request to protected data. This is called opaque token,
as it contains nothing. Just a random string. The implementation will be very different if I
need no login page, with a server-side frontend, a stateful application. But what if I want
a stateless application? With an Angular frontend? And some public pages? Let's start
implementing this simple example, and little by little, add the complicated extras. But first, I
will ask you to subscribe to my channel. A simple click that makes a difference to me. Let's start
with a stateful application with a server-side frontend. I start by creating the Spring Boot
application. I change the group, sergio, oauth2, backend. I select Maven, Java,
the latest version of Spring Boot, Java 17. And let's add some dependencies. I need web security, the OAuth2 client, Thymeleaf, and dev tools. Let's download and open it
with IntelliJ. Let's start by implementing the security. I start by creating a config
package where will be located all the configurations. And create the class which
contains the security configuration. security config. I annotate it with the
configuration and enable web security. I declare the bean security filter chain. With the HTTP security. Returns HTTP. Build. And now configure it authorize the HTTP request. I authorize any request as
authenticated. All the application will have the routes protected. I configure
the OAuth2 login. Customizer with defaults. This configures the OAuth2 login page based
on the properties file. I will add those configurations later. Let's continue now to
my first and unique endpoint to return the homepage. I will generate it from Spring Boot with
Thymeleaf. And as all the routes are protected, I need to authenticate my user to access the
homepage. So once authenticated, I have access to the user information and I will display it in
the homepage. Let's now create another package for the controllers. And I create the home controller.
I annotate it as a controller. And get on slash. String. Home. I inject the model and I use the
authentication principle annotation from OAuth2 user. Let's import the model. I just add one attribute, username from my user. Get attribute, name. And return the index. This returns the
indexed HTML page. Now let's create this index HTML. The homepage. It will be located in the
templates. Let's create a file index. HTML. Define the head. With a title. Spring Security OAuth2 login. And now, body, a
title OAuth2 login with Spring Security. You are successfully logged in with the name. Thymeleaf. text username. Let's add some style. Font weight bold. And one last thing for the backend, the
OAuth2 configuration. I change the application properties to application yaml. And add the OAuth2
configuration. Spring, Security, OAuth2, client, registration, Google. I need the client ID and the client secret. But I need to create
the client ID and client secret from Google. So let's go to the GCP console to create an
application. Once I create an account on GCP, I need to create an application that
will be displayed here. Let's create the OAuth2 client for this application to access the
protected data. I go to the API and services and credentials. Here I click on
create credentials. I choose the OAuth2 client. And now I select
the web application. I give a name, my fullstack app. And now I add the authorized
JavaScript Origins. It will be the current String Boot application at HTTP Localhost 8080. I also
add the Angular application I will use later HTTP, Localhost 4200. In the redirect URLs,
I add the default URL used by Google, HTTP Localhost 8080, logging, OAuth2, code,
Google. I will also add the URL for my Angular project. Click on create. And now I have the
client ID and the client secret. Let's copy both. Let's now start my application. When accessing the homepage at Localhost 8080, I'm
directly redirected to the Google login page. I select my account. And here is the homepage,
the index HTML, I've created. From Google, I receive the username which is displayed here.
This was the first step, configure the OAuth2 authentication with my Spring Boot application.
Now, I will edit my security configuration to allow some public pages. I start by setting my
homepage and all the routes on public as not protected. Request matches, the homepage, and
all under public, as permit all. I let the rest unchanged. And go to the homepage. I remove from
the homepage the OAuth2 user. As now it is public and it has no user. And now I will create
two controllers. A public one and a private one. Let's start by the private
one. Private controller. I add the controller annotation. Get
mapping on messages. Public, string, private, messages. And now, I
add again the authentication principal annotation and the model as before. I inject in the model. Just
the name in the body. User, get attribute, name. And return a response HTML page.
Let's go to the public controller. Public controller. I add again the
controller annotation. Get mapping, this time it must start with public.
Messages, public, string, public, messages. And I only inject the model. I add one attribute body, the same
but this time with the value nobody. And return the same page. Let's edit the index
HTML to read both the public and private content. I remove all these. And start.
H1, endpoint. H2, the private. Unlimited list. With a link to the private endpoint. HREF, at the messages endpoint, the
private messages endpoint. Private endpoint where Google auth is required.
And now I do the same with the public one. Public Public, public endpoint where no Authentication is required. And now the response with
the content of the public and private endpoints. A new HTML file.
Response dot HTML. Head, title, Spring Security OAuth2 logging response. Body. I add a link to come back to the homepage. Come back to home. A title. Response Pre with the span which contains the body. Let's test all together. I run. Here is the index HTML where I can see both
links, the private and the public one. If I click to the public endpoint, I have the response page
with nobody. I can go back with the link. And now I click on private. It redirects get again
to the login page of Google. I click on my username. And I am redirected to the response page
I've created. Now the username is displayed as the body. Now my application has public content
and private content. It's time to start with Angular. But when I use an external frontend,
Google will redirect to another URL. So the cookies won't work the same way. What I will do
is leave the resources server in the Sprint Boot application. It worked well with the access
token. But for the client server, I need to implement it by my own. I will use the Google API
to authenticate the user. Let's start updating the dependencies. I will add the dependency
Google API client. It comes from com Google API client. And the latest version is
220. I will replace the OAuth2 client by the OAuth2 resources server. And I will also add
another one, Spring Webflux. Reload the Maven project. As I have an external frontend, now I need to adapt the CORS. Let's
add this configuration. Web config. I add the configuration annotation. Declare
the bean. Public, web MVC configurer, CORS config. And only return, and only
return the configuration of the CORS. I choose the method add CORS mapping. Registry add mapping on route, I allow
the methods get, post and options. And allow the
origins HTTP localhost 4200. Now I update the configuration to use the
resources server instead of the client server. Client, security, resource server, opaque token, introspection URI, HTTPs googleapis.com.
And leave the client ID and client secret. I've been trying to configure Spring Security to
work with Google. But never succeeded. Instead, I found a way to make it work with a custom
introspector to read the user information once authenticated. So let's implement this custom
introspector. First I created a web client to call the Google API and obtain the users
information. Create a new class, web client config. This time is a component. Private
string introspect URI, which comes from the value, string, security, oauth2,
resource server, opaque token, introspect URI. I define the bean,
public, web client user info client, and return the web client. Builder, base URL, the introspect, and build.
That's it. And now I configure the Spring Security. I will add my web client. private final
web client user info client. I add the constructor to inject it. And now I start by the CORS
configuration. Customizer with defaults. I add an exception handling. Customizer, authentication
entry point. I return an HTTP status entry point, with the HTTP code, status unauthorized. This
adds a custom exception handling. Otherwise, if an exception occurs it redirect to the
homepage. Now the session management. Customizer, session creation policy, as stateless. I
leave the routes as they are. And change the OAuth2 configuration. I choose
the resources server, opaque token, customizer with defaults. And change the
client server to a resources server using the opaque token. I will add now another
bean. Public, opaque token introspector. And with the opaque token comes the custom introspector. Return new Google
opaque token introspector. And I send the user client. Let's
implement this introspector. I create the class in the config package. It
implements the opaque token with the web client. Private final web client. I
inject the client in the class. User info client. And override the introspect method. From
the frontend I sent a token in the authorization header. Then I receive it as an input parameter
in this method. So I call the Google API to get, with the URI builder, path, OAuth2, V3, user info. And I send as a query param the access token. Build. Retrieve the response.
Body to mono. To a class that I will create. User info. And block. Let's create this class into a new package DTOs. I'll create it as a record with the fields String sub, name, given name, family name. All those are fields
received by the access token. So I must respect the structure of the Google API.
Picture, the email image, verified and the locale. I use the web client to call the
Google API. Add the token as query param and receive the response into this object.
And now I return a new OAuth2 introspector authentication principle. With the user info,
the name, some attributes that I will build now. And no authorities. Let's build the attributes.
It will be a map of string and object. New hash map. I will just put the sub and the name. So my application is ready to
behave as a resources server. Let's implement the client server side using the Google API.
We saw before that when I tried to login, I have a link which redirects the user
to the Google authentication page. So I will create an endpoint to generate
this URL. A new controller. Auth controller. This time it will be a rest controller. I will inject the client ID and the client secret. From the value of the properties Spring, Security, OAuth2, Resource server,
opaque token and client ID. client secret. Now the endpoint to get the URL. Auth URL. That must be also public. I
will add it in the security configuration. Slash auth all. And return a
response entity. Which is a URL DTO. Let's create this URL
DTO as a record. In the DTO package. String URL. Nothing more. Here
I call the new Google authorization code request URL with the client ID, the redirect
URL, which is the frontend. Localhost 4200, the list of the scopes I want.
Arrays as list. The email, the profile, and the open ID. And
now I build. And this returns me the URL. Now I can return it in my DTO. This will generate the link where the user
can see the login form of Google. And this is the call back URL. I want Google to call my
frontend, as it's the one which manage the redirections. When Google redirects to
the frontend it sends a code with the URL. I need this code in the backend to obtain
the access token. So I create a new endpoint to handle this code. Auth callback. public
response entity. This time it returns a token dto. Let's create this dto as
a record. In the DTOs package. String token. And I receive as a
request parameter code, the code. And now I call another one. Google authorization
code token request. This time I must specify the HTTP transport, net HTTP transport. Now the
Json Factory. New Json Factory. The client ID, the client secret, my code, the callback URL again. And now I execute. And get only the access token. And read token. Surround with a try catch. I return response entity. With the status HTTP status unauthorized. And if everything wents okay, I return
the response entity, okay, with my token. This token is the one sent back
to the frontend. And the frontend will use it in the authorization header.
Let's now adapt the public and private endpoints. There will be now rest controllers. Which returns
response entity of message DTO. Let's create this record. Return response entity,
okay, message dto. With private content. With the name. With
authentication principle, now I read only one field,
expression the name. String name. The name is read from
the access token and directly injected in this method. Let's now
edit the public endpoint. A rest controller. As before, it returns
a response entity of message DTO. But this time, I have no authentication principle
annotation, so it returns response entity okay message DTO, with public content. Nothing
more. I have my backend ready. Configured as a resources server. And the authentication
done with Google manually. Now I'm going to configure the frontend. A simple application
with Angular. it will contain a login button to display the URL, a public content and
the private content. Let's go. NG new frontend. I will use the default settings. So, no, CSS. And wait until the node packages
are installed. Let's now generate some few components. NG generate component
login form. To display the URL from Google. NG generate component public
content. To display the public content. Private content. To display the private
content. And NG generate a service. My HTTP client. To include in all the requests the token,
the authentication token. In the root component, I will add the logic to switch between the
login URL, the public content and the private content. Let's open the HTML and remove everything. I start with a body and
a title. My awesome app. Now a simple button which redirects to the
login. And now display all the components. App login form. App
public content. And app private content. I will display the login form only if,
NG if, a viable component to show is equal to login. For the public content, the variable
must be public. And for the private content the variable must be private. And
when I click on the login button, I change the value of the variable to login. The
public content will be displayed by default. When clicking on the login button, I want to display
the Google login URL. And once authenticated, I display the private content.
Let's go now to the typescript file. I create the variable, string, with
the default value public. I create the constructor with the HTTP client, with my HTTP client. And the route dependency. This one
will be used to read the code in the URL. So, when loading the frontend, in the NG on
init method, I will check if there is a code in the URL. This route. I read the query
params .And subscribe to read the values. Params. If there is a parameter
named code and it is not undefined, I request the token. And when requesting
the token, if the result is true I displayed the private
content. Otherwise I display the public content. Let's
now edit the public content HTML. I start with a title ,public content, and a paragraph with the content.
Nothing more. Let's go to the typescript. I create the variable content, initialized empty. In the constructor, I will inject the HTTP
client, my HTTP service. And when loading the component, I call the backend to
request the public content. HTTP get public messages. This
is the URL where I get the public content. I subscribe to the
response. It will be a data of type message. And I save the result in the
content variable. Let's create this class. Export class message. Constructor
with just one field, message. It's a string. Let's import it. Message from
message. I will also add the HTTP service, my HTTP client service. Let's add the same
import in the root component. And continue with the private component. I start with the
HTML. As before, div with a title. Private content. And the paragraph with the content. In
the typescript, I also inject the HTTP service. Constructor private http my HTTP service. I
initialize the content empty. And when loading the component, as before, I request the backend. But this time, I request the
private endpoint, which is messages. Subscribe to response, data message again. And the result will be saved in the content variable. I also add the message import. That's it. My public and private content
components are ready. Let's go to the login form component. In the HTML, I add a title login form. And a
link, href, with a URL. Sign in with Google. That's it. Let's go to the typescript
and request this URL. I will import the HTTP client. Initialize the URL variable empty. Inject the HTTP client in the constructor. And when loading the component,
I request the backend to obtain the URL. In the endpoint auth URL. Now,
subscribe to the response. Data any. And save the result in the URL. Okay my three
components are ready. Let's go now to the HTTP service. I start by creating a token variable
initialized empty where will be saved the token to access the private content
of the backend. I inject the real HTTP client. Let's import it. From angular common HTTP. And create the
three methods. The get to obtain the public content. Return this HTTP get. The URL of the backend. Get private to read the
private content, the protected content. Return this HTTP get Localhost 8080 URL. With the headers this time. New HTTP headers. Let's import this one. With authorization bearer plus the token.
Let's add in the module the HTTP client. Import HTTP client
module, from angular common HTTP. I we also add the router module.
Router module from angular router. This one is to read the query
parameters from the URL. For route. And now implement the latest
method to obtain the token from the backend. Get token. I send the code and return an observable. Return this HTTP get token from HTTP Localhost
8080, auth callback with the code. And add a parameter, observe
response. This is to obtain an observable. Pipe, the response, which is an HTTP response of token. If response status is
200 and the response body is not null, save the token. Response body token. Then return true. Otherwise return
false. Let's create the token object. Token.TS. Export class token.
Constructor with only one field. Public token. That's it. Let's add the missing Imports. HTTP response token observable. From RxJs. And map from from RxJs
operators. Let's now test all together. Here is the homepage where I have the login button and the
public content displayed by default. If I click on the login button, I have the URL to login with
Google. Let's click. It redirects me to the login form of Google. I select my account. And I am
redirected again to the homepage with with a code in the URL. Now I can load the private content
where I displayed the private message which is my name, the username of the authenticated
user. With the most basic configuration, I have a complete protected application by
Google. With a server side rendering frontend and stateful. Then I changed the security
configuration to allow some public pages. But still a server sides rendering frontend and
stateful. Finally I've converted my backend to a resources server, and I handled the login
with Google, with the Google API. This way, I create an Angular frontend and a stateless
backend. That's all for this video. I hope you liked it. Please click on the like button,
subscribe to my channel. And see you soon. Bye!