Introduction
Click here to source code
In mobile application ask your user to enter username/password directly through your interface and storing the credentials in the app can be exposed by the user. In the case of Android, they can completely decompile your app and easily retrieve them. Imagine the problem when the password are faceebook, twitter, etc. One way to resolve this is use oauth authentication.
In mobile application ask your user to enter username/password directly through your interface and storing the credentials in the app can be exposed by the user. In the case of Android, they can completely decompile your app and easily retrieve them. Imagine the problem when the password are faceebook, twitter, etc. One way to resolve this is use oauth authentication.
Oauth2 advantages:
- The client submits an authorization request to the server, which validates that the client is a legitimate client of its service.
- The server redirects the client to the content provider to request access to its resources.
- The content provider validates the user's identity, and often requests their permission to access the resources.
- The content provider redirects the client back to the server, notifying it of success or failure. This request includes an authorization code on success.
- The server makes an out-of-band request to the content provider and exchanges the authorization code for an access token.
- The client can revoke an access token.
- Credentials are never exposed (user and password).
This sample wants show how protect server resources using Spring OAuth 2.0 and request resources from mobile hybrid applications clients. The provider does this by managing and verifying the OAuth 2.0 tokens used to access the protected resources. Where
applicable, the provider must also supply an interface for the user to
confirm that a client can be granted access to the protected resources.
The goals are the next:
- Make a oauth2 authentication server.
- Make a oauth2 protected resources server.
- Uses GitHub (social network authorization), for user authorization from application mobile.
- Store token in MongoDb. By default it creates tokens via random value and handles everything except for the persistence of the tokens which it delegates to a TokenStore. The default store is an in-memory implementation, but shouldn't be used in production time. Store token in database allows recover token when shutdown system and share a database between servers, either scaled up instances of the same server if there is only one, or the Authorization and Resources Servers if there are multiple components.
- Use implicit grant type. The implicit grant type is used to obtain access tokens (it does not support the issuance of refresh tokens) and is optimized for public clients known to operate a particular redirection URI. These clients are typically implemented in a browser using a scripting language such as JavaScript. As a redirection-based flow, the client must be capable of interacting with the resource owner's user-agent (typically a web browser) and capable of receiving incoming requests (via redirection) from the authorization server. Unlike the authorization code grant type in which the client makes separate requests for authorization and access token, the client receives the access token as the result of the authorization request. The implicit grant type does not include client authentication, and relies on the presence of the resource owner and the registration of the redirection URI. Because the access token is encoded into the redirection URI, it may be exposed to the resource owner and other applications residing on the same.
- Saving profile information when the user is logged from application mobile and can recovery that information when the user requests a resource to the server.
- Allows revoke token from mobile application. This feature is important when the mobile device is lost. In this case is necessary revoke access and come back login.
Before continue I recommend read the next articles:
- https://projects.spring.io/spring-security-oauth/docs/oauth2.html
- https://tools.ietf.org/html/draft-ietf-oauth-v2-31
- https://spring.io/guides/tutorials/spring-boot-oauth2/
Implementation
The implementation is divided in several steps.
Configure the oauth2 authentication server
We mark the configuration class with @EnableAuthorizationServer attribute. In my case, the class is called OAuth2AuthorizationServerConfig.
Configuring client details:
clientId
: (required) -> "mobileclient".scope
: The scope to which the client is limited. If scope is undefined or empty (the default) the client is not limited by scope -> "trust".authorizedGrantTypes
: Grant types that are authorized for the client to use -> "implicit". Using implicit the clients are typically implemented in a browser using a scripting language such as JavaScript. As a redirection-based flow, the client must be capable of interacting with the resource owner's user-agent (typically a web browser) and capable of receiving incoming requests (via redirection) from the authorization server.
Configuring the store system for tokens. The default store is an in-memory implementation, but shouldn't be used in production time. Store token in database allows recover token when shutdown system and share a database between servers, either scaled up instances of the same server if there is only one, or the Authorization and Resources Servers if there are multiple components.
I have created a service to store tokens using MongoDB. Each time more MongoDB is used to manage information.
MongoDBTokenStore use two repositories to controller token (OAuth2AccessTokenRepository) and refresh token (OAuth2RefreshTokenRepository).
To save profile information, I going to use a service of DefaultTokenServices, that allowed me intercept the token generation, before system store the token in mongodb.
CustomTokenEnhancer allows add additional information on the response returned by the authorization server when it issues access tokens. I could store the additional information in my client or getting this information in my resources server, We will see it later.
ProfileConfiguartionService saves profile information. The profile has two key fields:
- User authentication client identifier: Identify the social provider to authentication. In my case GitHub, but could have several providers.
- User name: User name logged in each provider.
Configure the user authorization server
File: WebSecurityConfig.javaFile: index.html
Based on Spring Boot and OAuth2
We has protected the resources server, but who protects the oauth authentication server. If we don't do nothing more, everybody could request token using /oauth/authorize endpoint
.
Therefore, we need protect the oauth authentication server. We will use standard spring security.
Specifically, we going to add a link to authenticate with github as authorization provider, although we could add severals links so the user can choose what provider use.
We add an additional security filter to
handle the "/login/github" requests coming from our new link. We
already have a custom authentication filter for github created in our ssoFilter()
In this moment, also needs to know about the client registration with Github and to complete the authentication it needs to know where the user info endpoint. This information is recovered from application.yaml file using @ConfiguarationProperties. You should create you oauth application in github and fill in with your own information into application.yaml:
Warning: When you create github oauth application, you will have set authorization callback URL to http://<local ip>:8080/login/github
Is necessary annotate the class with @EnableOAuth2Client attribute if we want create OauthRestTemplate using Oauth2ClientContext.
method.In this moment, also needs to know about the client registration with Github and to complete the authentication it needs to know where the user info endpoint. This information is recovered from application.yaml file using @ConfiguarationProperties. You should create you oauth application in github and fill in with your own information into application.yaml:
Warning: When you create github oauth application, you will have set authorization callback URL to http://<local ip>:8080/login/github
Is necessary annotate the class with @EnableOAuth2Client attribute if we want create OauthRestTemplate using Oauth2ClientContext.
All that is needed is to wire the filter up so that it gets called in
the right order in our Spring Boot application. To do that we need FilterRegisrationBean. We autowire the already available filter, and register it with a sufficiently low order that it comes before
the main Spring Security filter. In this way we can use it to handle
redirects signaled by exceptions in authentication requests.
To finish the Authorization Server we just need to provide security
configuration for its UI. In fact there isn’t much of a user interface
in this simple app, but we still need to protect the
/oauth/authorize endpoint, and make sure that the home page with the "Login" buttons is visible. Any URL except "/" and "/login" are protected. That’s why we have this method:Configure the resources server
File: ResourceServerConfiguration.javaFile: ResourceServer.java
We configure here the resources permissions. In ours case, /hello and /revokeToken only accept request for "trust" scope In addition, only accept request for resources with id "mobileclient". We can remember when we configured the oath2 autentication server, we set resourcesIds to "mobileclient".
One time that we have configured the resources server, we create the resource endpoint:
- /hello: We get authentication information using OAuth2Authentication, and we get profile information from repository using profileId, which we have recovered from token additional information.
- /revokeToken: Revoke token using ConsumerTokenServices.
Mobile application
File: page1.ts
Before of starting, if we want test the application is neccesary an emulator as Genymotion. The motive is because when use cordova plugin, doesn't work in the browser.
We need get a token for can access resources server. To get token we need authorization in ours server. How can I do it from hybrid application?. If we remember the oauth2 dance. See figure under, we need calback address to return token. That's possible using a external browser. Ionic mobile application can use the external browser via cordova plugin. We going to use InAppBrowser plugin.
Before of starting, if we want test the application is neccesary an emulator as Genymotion. The motive is because when use cordova plugin, doesn't work in the browser.
We need get a token for can access resources server. To get token we need authorization in ours server. How can I do it from hybrid application?. If we remember the oauth2 dance. See figure under, we need calback address to return token. That's possible using a external browser. Ionic mobile application can use the external browser via cordova plugin. We going to use InAppBrowser plugin.
When we hit login button, we connect with authorization server using the next address:
http://192.168.1.102:8080/oauth/authorize?client_id=mobileclient&redirect_uri=http://localhost/callback&response_type=token"
http://192.168.1.102:8080/oauth/authorize?client_id=mobileclient&redirect_uri=http://localhost/callback&response_type=token"
The authorization server detects /oauth/authorize isn't authenticated, and redirect request to login page. In ours case github. When we introduce username and password into github, spring security send a token to redirect_uri, in ours case "http://localhost/callback".
The question is: How do we intercept the call to "http://localhost/callback"?. The answer is using the inaappbrowser event.
The "loadstart" event allows capture the reponse into browser. When we receive the response splits the url and recovering the token. At this moment, we store the token in a service called TokenStore. In production application will be better to store token in encrypted local storage.
In the next screen can see the token assigned:
The next is call to /hello and /revokeToken resources from page1 and page2.
There is a service which do that functions called ServerService. The most important is when we send the request, we must set the header with the token: 'Authorization', 'Bearer ' + this.tokenStore.token
Even we can revoke the token:
After of revoking the token, if We try call GetHello resource the resource server return an error:
We will have that come back login to get token.