How to Create a Custom SMS Code Grant Type in Spring OAuth2

This tutorial explains how to extend Spring Security's OAuth2 authorization server by implementing a custom SMS‑code grant type, covering the necessary configuration classes, bean definitions, token store setup, and client usage examples.

Architect's Alchemy Furnace
Architect's Alchemy Furnace
Architect's Alchemy Furnace
How to Create a Custom SMS Code Grant Type in Spring OAuth2

Overview

The article builds on a previous discussion of Spring OAuth2's four standard grant types (Authorization Code, Implicit, Password, Client) and shows how to create a custom grant type when those do not satisfy business requirements.

Spring Security’s extensibility is demonstrated by extending AuthorizationServerConfigurerAdapter and overriding three key configuration methods:

configure(ClientDetailsServiceConfigurer clients)
configure(AuthorizationServerEndpointsConfigurer endpoints)
configure(AuthorizationServerSecurityConfigurer oauthServer)

The second method, AuthorizationServerEndpointsConfigurer, provides a rich API ( AuthorizationServerEndpointsConfigurer) with many configurable options. The most relevant ones for the custom grant are:

6 – authenticationManager(AuthenticationManager) 13 – tokenGranter(TokenGranter) 14 – tokenServices(AuthorizationServerTokenServices) 15 – tokenStore(TokenStore) 16 – userDetailsService(UserDetailsService) Example bean definitions for password encoding and global user details:

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

@Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(myUserDetailsService())
        .passwordEncoder(passwordEncoder());
}

Token storage can be configured to use Redis:

@Bean
public RedisTemplateTokenStore tokenStore() {
    return new RedisTemplateTokenStore(redisTemplate);
}

To illustrate the default grant implementations, the source of ResourceOwnerPasswordTokenGranter is shown, highlighting its inheritance from AbstractTokenGranter and the overridden getOAuth2Authentication method.

Creating a custom SMS‑code grant involves defining a new class that extends AbstractTokenGranter:

public class SMSCodeTokenGranter extends AbstractTokenGranter {
    private static final String GRANT_TYPE = "sms_code";
    private final AuthenticationManager authenticationManager;
    private MyUserDetailsService myUserDetailsService;
    private StringRedisTemplate stringRedisTemplate;

    public SMSCodeTokenGranter(AuthenticationManager authenticationManager,
                               AuthorizationServerTokenServices tokenServices,
                               ClientDetailsService clientDetailsService,
                               OAuth2RequestFactory requestFactory,
                               MyUserDetailsService myUserDetailsService,
                               StringRedisTemplate stringRedisTemplate) {
        this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        this.myUserDetailsService = myUserDetailsService;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
        Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
        String userName = parameters.get("userName");
        String userPhone = parameters.get("userPhone");
        String smsCode = parameters.get("smsCode");
        UserDetails user = myUserDetailsService.loadUserByUsername(userName);
        if (user == null) {
            throw new InvalidGrantException("用户不存在");
        }
        String cachedCode = stringRedisTemplate.opsForValue().get("sms:vc:" + userPhone);
        if (StringUtils.isBlank(cachedCode)) {
            throw new InvalidGrantException("用户没有发送验证码");
        }
        if (!smsCode.equals(cachedCode)) {
            throw new InvalidGrantException("验证码不正确");
        }
        Authentication userAuth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
        OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
        return new OAuth2Authentication(storedOAuth2Request, userAuth);
    }
}

The custom grant must be added to the list of token granters returned by getDefaultTokenGranters() and the composite token granter must delegate to this extended list:

tokenGranters.add(new SMSCodeTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory, myUserDetailsService, stringRedisTemplate));

Finally, the endpoint configuration registers the custom tokenGranter:

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.tokenStore(tokenStore())
             .userDetailsService(myUserDetailsService)
             .tokenGranter(tokenGranter())
             .authenticationManager(authenticationManager);
    endpoints.tokenServices(defaultTokenServices());
}

Usage example with RestTemplate shows how to request a token using the new sms_code grant type:

RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String, String> request = new LinkedMultiValueMap<>();
request.add("client_id", loginParam.getClientId());
request.add("client_secret", loginParam.getClientSecret());
request.add("grant_type", "sms_code");
request.add("user_name", userInfoVo.getUserName());
request.add("user_phone", loginParam.getUserPhone());
request.add("sms_code", loginParam.getSmsCode());
Object response = restTemplate.postForObject(authServiceUrl + "/oauth/token", request, Object.class);

Constants for the request parameters are defined as static final strings (e.g., KEY_CLIENT_ID = "client_id").

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaspringOAuth2Custom GrantSMS Authentication
Architect's Alchemy Furnace
Written by

Architect's Alchemy Furnace

A comprehensive platform that combines Java development and architecture design, guaranteeing 100% original content. We explore the essence and philosophy of architecture and provide professional technical articles for aspiring architects.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.