-
Notifications
You must be signed in to change notification settings - Fork 7
Custom Authentication
Authentication mechanisms can provide access tokens by key, link, LDAP login and password, etc. REST API uses its own authentication mechanism that cannot be modified. In order to use custom authentication process, you need to create a REST controller and use its URL.
Let’s consider the custom authentication mechanism that enables getting an OAuth token by a promo code. In the following example we will use a sample application that contains the Coupon
entity with code
attribute. We will send this attribute’s value as an authentication parameter in GET request.
- Create a
Coupon
entity with the code attribute:
@Column(name = "CODE", unique = true, length = 4)
protected String code;
-
Create a user with promo-user login on behalf of which the authentication will be performed.
-
Create a new Spring configuration file with name
rest-dispatcher-spring.xml
under the root package (com.company.demo) of web module. The content of the file must be as follows:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<context:component-scan base-package="com.company.demo.web.rest"/>
</beans>
- Include the file into the
cuba.restSpringContextConfig
application property in themodules/web/src/web-app.properties
file:
cuba.restSpringContextConfig = +com/company/demo/rest-dispatcher-spring.xml
- Create the rest package under the root package of web module and implement the custom Spring MVC controller in it. Use the
OAuthTokenIssuer
bean to generate and issue the REST API token for a user after the custom authentication:
@RestController
@RequestMapping("auth-code")
public class AuthCodeController {
@Inject
private OAuthTokenIssuer oAuthTokenIssuer;
@Inject
private LoginService loginService;
@Inject
private Configuration configuration;
@Inject
private DataManager dataManager;
@Inject
private MessageTools messageTools;
// here we check secret code and issue token using OAuthTokenIssuer
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity get(@RequestParam("code") String authCode) {
// obtain system session to be able to call middleware services
WebAuthConfig webAuthConfig = configuration.getConfig(WebAuthConfig.class);
UserSession systemSession;
try {
systemSession = loginService.getSystemSession(webAuthConfig.getTrustedClientPassword());
} catch (LoginException e) {
throw new RuntimeException("Error during system auth");
}
// set security context
AppContext.setSecurityContext(new SecurityContext(systemSession));
try {
// find coupon with code
LoadContext<Coupon> loadContext = LoadContext.create(Coupon.class)
.setQuery(LoadContext.createQuery("select c from demo$Coupon c where c.code = :code")
.setParameter("code", authCode));
if (dataManager.load(loadContext) == null) {
// if coupon is not found - code is incorrect
return new ResponseEntity<>(new ErrorInfo("invalid_grant", "Bad credentials"), HttpStatus.BAD_REQUEST);
}
// generate token for "promo-user"
OAuthTokenIssuer.OAuth2AccessTokenResult tokenResult =
oAuthTokenIssuer.issueToken("promo-user", messageTools.getDefaultLocale(), Collections.emptyMap());
OAuth2AccessToken accessToken = tokenResult.getAccessToken();
// set security HTTP headers to prevent browser caching of security token
HttpHeaders headers = new HttpHeaders();
headers.set(HttpHeaders.CACHE_CONTROL, "no-store");
headers.set(HttpHeaders.PRAGMA, "no-cache");
return new ResponseEntity<>(accessToken, headers, HttpStatus.OK);
} finally {
// clean up security context
AppContext.setSecurityContext(null);
}
}
// POJO for JSON error messages
public static class ErrorInfo implements Serializable {
private String error;
private String error_description;
public ErrorInfo(String error, String error_description) {
this.error = error;
this.error_description = error_description;
}
public String getError() {
return error;
}
public String getError_description() {
return error_description;
}
}
}
- Exclude the
rest
package from scanning in web/core modules: theOAuthTokenIssuer
bean is available only in REST API context, and scanning for it in the application context will cause an error.
<context:component-scan base-package="com.company.demo">
<context:exclude-filter type="regex" expression="com\.company\.demo\.web\.rest\..*"/>
</context:component-scan>
Now users will be able to obtain OAuth2 access code using GET HTTP request with the code parameter to
http://localhost:8080/app/rest/auth-code?code=A325
The result will be:
{
"access_token": "74202587-6c2b-4d74-bcf2-0d687ea85dca","token_type":"bearer",
"expires_in":43199,
"scope":"rest-api"
}
The obtained access token should then be passed to REST API, as described in the documentation.
- Home
- Predefined JPQL Queries Configuration
- Services Configuration
- Data Model Versioning
- CORS Settings
- Anonymous Access
- Other REST API Settings
- Creating Custom OAuth2 Protected Controllers
- Security Constraints for Collection Attributes
- Persistent Token Store
- Project-specific Swagger Documentation
- Application Properties
-
Using REST API
- Getting an OAuth Token
- REST API Authentication with LDAP
- Custom Authentication
- Getting an Entity Instances List
- New Entity Instance Creation
- Existing Entity Instance Update
- Executing a JPQL Query (GET)
- Executing a JPQL Query (POST)
- Service Method Invocation (GET)
- Service Method Invocation (POST)
- Files Downloading
- Files Uploading
- JavaScript Usage Example
- Getting Localized Messages
- Data Model Versioning Example
- Using Entities Search Filter