OAuth2 and Google to Protect Your Spring Security and Angular Application

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
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!
Info
Channel: The Dev World - by Sergio Lema
Views: 9,111
Rating: undefined out of 5
Keywords: java, spring security, oauth2, OAuth2 and Google to Protect Your Spring Security and Angular Application
Id: yp28g5AJczM
Channel Id: undefined
Length: 48min 12sec (2892 seconds)
Published: Mon Dec 11 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.