You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.adoc
+69-35Lines changed: 69 additions & 35 deletions
Original file line number
Diff line number
Diff line change
@@ -1,54 +1,62 @@
1
-
= Securing a Web Application with OAuth2/OpenId Connect
1
+
= Securing a Web Application with OAuth2/OpenID Connect
2
2
:page-permalink: /
3
3
:page-github: vertx-howtos/web-and-oauth2-oidc
4
4
:author: Paulo Lopes <pmlopes@gmail.com>
5
5
:toc:
6
6
7
-
This how to will show you how to build and secure a simple web vert.x application with Oauth2 and OpenId Connect.
7
+
You will learn how to build and secure a simple web application with Vert.x, OAuth2 and OpenID Connect.
8
8
9
9
== What you will build
10
10
11
-
In the first part of the how-to, you will build a secure web application that will use GitHub to authenticate any application user. We will then continue exploring the API and use OpenId Connect to auto discover the security related configuration of the application.
11
+
In the first part of the how-to, you will build a secure web application that will use GitHub to authenticate any application user.
12
+
We will then continue exploring the API and use OpenID Connect to auto discover the security related configuration of the application.
12
13
13
14
== What you need
14
15
15
16
* A text editor or IDE
16
-
* Java 8 or higher (11 recommended for the extra security algorithms)
17
-
* GitHub account
17
+
* Java 11 or higher
18
+
* A GitHub account
18
19
19
20
== Create a Project
20
21
21
22
Go to start.vertx.io and https://start.vertx.io/starter.zip?groupId=howto&artifactId=oauth-oidc&vertxDependencies=vertx-web&vertx-auth-oauth2,vertx-web-templ-handlebars,vertx-web-client[create a project] with the following dependencies:
22
23
23
24
* Vert.x Web
24
-
* Oauth2
25
+
* OAuth2
25
26
* Handlebars template engine
26
27
* Vert.x Web Client
27
28
28
29
image::project.png[width=600]
29
30
30
31
== Basics of Authentication
31
32
32
-
In this section, we're going to focus on the basics of authentication. Specifically, we're going to create a Java server that implements GitHub's https://docs.github.com/en/developers/apps/authorizing-oauth-apps[web application flow].
33
-
33
+
In this section, we're going to focus on the basics of authentication.
34
+
Specifically, we're going to create a Java server that implements GitHub's https://docs.github.com/en/developers/apps/authorizing-oauth-apps[web application flow].
34
35
35
36
== Registering your app
36
37
37
-
First, you'll need to https://github.com/settings/applications/new[register your application]. Every registered OAuth2 application is assigned a unique **Client ID** and **Client Secret**. The Client Secret should **NOT** be shared! That includes checking the string into your repository.
38
-
39
-
You can fill out every piece of information however you like, except the **Authorization callback URL**. This is easily the most important piece to setting up your application. It's the callback URL that GitHub returns the user to after successful authentication.
38
+
First, you'll need to https://github.com/settings/applications/new[register your application].
39
+
Every registered OAuth2 application is assigned a unique **Client ID** and **Client Secret**.
40
+
The Client Secret should **NOT** be shared!
41
+
That includes checking the string into your repository.
40
42
41
-
Since we're running a regular Vert.x Web server, the location of the local instance is set to `http://localhost:8080`. Let's fill in the callback URL as `http://localhost:8080/callback`.
43
+
You can fill out every piece of information however you like, except the **Authorization callback URL**.
44
+
This is easily the most important piece to setting up your application.
45
+
It's the callback URL that GitHub returns the user to after successful authentication.
42
46
47
+
Since we're running a regular Vert.x Web server, the location of the local instance is set to `http://localhost:8080`.
48
+
Let's fill in the callback URL as `http://localhost:8080/callback`.
43
49
44
50
== Accepting user authorization
45
51
46
-
Now, let's start filling out our simple server. Open the class `howto.oauth_oidc.MainVerticle` and paste this into it:
52
+
Now, let's start filling out our simple server.
53
+
Open the class `howto.oauth_oidc.MainVerticle` and paste this into it:
<7> For this resource we require that users have the authority to retrieve the user emails
59
67
<8> Start up the server
60
68
61
-
Your client ID and client secret keys come from https://github.com/settings/developers[your application's configuration page]. You should **NEVER**, **EVER** store these values in your git repository -- or any other public place, for that matter. We recommend storing them as http://en.wikipedia.org/wiki/Environment_variable#Getting_and_setting_environment_variables[environment variables] -- which is exactly what we've done here.
69
+
Your client ID and client secret keys come from https://github.com/settings/developers[your application's configuration page].
70
+
You should **NEVER**, **EVER** store these values in your git repository -- or any other public place, for that matter.
71
+
We recommend storing them as http://en.wikipedia.org/wiki/Environment_variable#Getting_and_setting_environment_variables[environment variables] -- which is exactly what we've done here.
62
72
63
-
Notice that the protected resource uses the scope `user:email` to define the scopes requested by the application. For our application, we're requesting `user:email` scope for reading private email addresses later in the how-to.
73
+
Notice that the protected resource uses the scope `user:email` to define the scopes requested by the application.
74
+
For our application, we're requesting `user:email` scope for reading private email addresses later in the how-to.
64
75
65
76
Next, in the project `resources` create the template `views/index.hbs` and paste this content:
66
77
@@ -75,12 +86,14 @@ Navigate your browser to http://localhost:8080. After clicking on the link, you
75
86
76
87
image::authorize.png[width=600]
77
88
78
-
After a successful app authentication, GitHub provides a temporary code value. This code is then posted back to GitHub in exchange for an `access_token` which is in turn translated to a `User` instance in your vert.x application. All this is taken care for you by the handler.
79
-
89
+
After a successful app authentication, GitHub provides a temporary code value.
90
+
This code is then posted back to GitHub in exchange for an `access_token` which is in turn translated to a `User` instance in your Vert.x application.
91
+
All this is taken care for you by the handler.
80
92
81
93
== Checking granted scopes
82
94
83
-
Before the `User` object is handled to you, if your handler was configured with `authorities` they will be first checked. If they are not present then the whole process is aborted with an `Authorization (403)` error.
95
+
Before the `User` object is handled to you, if your handler was configured with `authorities` they will be first checked.
96
+
If they are not present then the whole process is aborted with an `Authorization (403)` error.
84
97
85
98
However you might want to assert for other granted authorities, in this case you would add a intermediate handler such as:
<1> Create a kind of authorization, in this case it's a Permission
95
109
<2> The `permission` we want to assert
96
110
<3> The `provider` object will extract the right data from the user and perform the assertion
97
111
98
112
Case the assertion fails, the router will stop executing and return a `Forbidden` error.
99
113
100
-
101
114
== Making authenticated requests
102
115
103
-
At this moment your application is already secure, and you can execute handlers knowing that the users are real GitHub users. You can now execute API calls on behalf of the user. For example, we could update the protected resource to print out the user registered email addresses, and some basic profile information from the `userInfo` end point.
116
+
At this moment your application is already secure, and you can execute handlers knowing that the users are real GitHub users.
117
+
You can now execute API calls on behalf of the user.
118
+
For example, we could update the protected resource to print out the user registered email addresses, and some basic profile information from the `userInfo` end point.
It'd be a pretty bad model if we required users to log into the app every single time they needed to access the web page. For example, try navigating directly to http://localhost:8080/protected. You'll get an authentication request over and over.
142
+
It'd be a pretty bad model if we required users to log into the app every single time they needed to access the web page.
143
+
For example, try navigating directly to http://localhost:8080/protected.
144
+
You'll get an authentication request over and over.
126
145
127
-
What if we could circumvent the entire "click here" process, and just remember that, as long as the user's logged into GitHub, they should be able to access this application? Hold on to your hat, because that's _exactly what we're going to do_.
146
+
What if we could circumvent the entire "click here" process, and just remember that, as long as the user's logged into GitHub, they should be able to access this application?
147
+
Hold on to your hat, because that's _exactly what we're going to do_.
128
148
129
-
Our little server above is rather simple. In order to wedge in some intelligent authentication, we're going to switch over to using sessions for storing tokens. This will make authentication transparent to the user.
149
+
Our little server above is rather simple.
150
+
In order to wedge in some intelligent authentication, we're going to switch over to using sessions for storing tokens.
151
+
This will make authentication transparent to the user.
130
152
131
153
This can be achieved with the stock handlers, so our server file would be:
<1> A session handler using in memory storage will now be able to keep track of active users and you will not need to re-login on each request.
138
161
139
162
140
163
=== Why persistence is important?
141
164
142
-
While it may sound better to keep no state on the server, persistence has some benefits over stateless. When a session is available, your application will be safer. The reason is that Oauth2 uses `nonce/state` values during calls that can only be properly validated when a session is in place. With a session we ensure that `nonce` values are unique and not reusable so your application is protected against replay attacks.
165
+
While it may sound better to keep no state on the server, persistence has some benefits over stateless.
166
+
When a session is available, your application will be safer.
167
+
The reason is that OAuth2 uses `nonce/state` values during calls that can only be properly validated when a session is in place.
168
+
With a session we ensure that `nonce` values are unique and not reusable so your application is protected against replay attacks.
143
169
144
-
A second layer of optional protection is the use of https://datatracker.ietf.org/doc/html/rfc7636[Proof Key for Code Exchange]. PKCE adds another layer of security to the exchanges between your application and the Oauth2 server to enable it you only need to configure your handler as:
170
+
A second layer of optional protection is the use of https://datatracker.ietf.org/doc/html/rfc7636[Proof Key for Code Exchange].
171
+
PKCE adds another layer of security to the exchanges between your application and the OAuth2 server to enable it you only need to configure your handler as:
<1> By specifying a length between 64 and 128 PKCE will be enabled
154
180
181
+
<1> By specifying a length between 64 and 128 PKCE will be enabled
155
182
156
183
== OpenID Connect
157
184
158
-
Until this moment, we have been covering, plain `OAuth2`. Vert.x also allows you to use https://openid.net/connect/[OpenID Connect].
185
+
Until this moment, we have been covering, plain `OAuth2`.
186
+
Vert.x also allows you to use https://openid.net/connect/[OpenID Connect].
159
187
160
-
In a nutshell, OpenID Connect is a simple identity layer on top of the OAuth 2.0 protocol. The main differences are that tokens, are not opaque strings, but encoded in JSON Web Token format. This allows applications to have more fine-grained control on permissions/roles and reduce the number of round-trips to the IdP server. This also means that you will need to know much more information at front to start the application. For example, a few extra HTTP endpoints, security keys, etc...
188
+
In a nutshell, OpenID Connect is a simple identity layer on top of the OAuth 2.0 protocol.
189
+
The main differences are that tokens, are not opaque strings, but encoded in JSON Web Token format.
190
+
This allows applications to have more fine-grained control on permissions/roles and reduce the number of round-trips to the IdP server.
191
+
This also means that you will need to know much more information at front to start the application.
192
+
For example, a few extra HTTP endpoints, security keys, etc...
161
193
162
-
Albeit this looking more complex, OpenID, defines a discovery API, which simplifies the setup to just a few lines of code. Instead of having you to know all the properties you can just (for example) discover the configuration if you're using https://www.keycloak.org/[keycloak]:
194
+
Albeit this looking more complex, OpenID, defines a discovery API, which simplifies the setup to just a few lines of code.
195
+
Instead of having you to know all the properties you can just (for example) discover the configuration if you're using https://www.keycloak.org/[keycloak]:
<1> Keycloak can host multiple applications so we can specify a tenant name
169
203
<2> Keycloak server URL
170
204
171
-
The discovery process will perform the configuration of all known HTTP endpoints, and load security keys used to validated tokens. Once ready an instance of `OAuth2Auth` is returned like before. It is important here that you did not have to load and configure all this manually.
205
+
The discovery process will perform the configuration of all known HTTP endpoints, and load security keys used to validated tokens.
206
+
Once ready an instance of `OAuth2Auth` is returned like before.
207
+
It is important here that you did not have to load and configure all this manually.
172
208
173
209
`Discovery` is a standard so you can use it with other services (that support it), for example (in no particular order):
174
210
@@ -178,20 +214,18 @@ The discovery process will perform the configuration of all known HTTP endpoints
178
214
* Amazon Incognito
179
215
* etc...
180
216
181
-
182
217
== Summary
183
218
184
219
In this how-to we covered:
185
220
186
221
1. Creating a web project
187
-
2. Secure a web application with Oauth2
188
-
3. Invoke secure APIs with WebClient and Oauth2
222
+
2. Secure a web application with OAuth2
223
+
3. Invoke secure APIs with WebClient and OAuth2
189
224
4. Persist user session data
190
225
5. Use OpenID Connect
191
226
192
227
I hope you now can use OAuth2 on your next project!
0 commit comments