com.nimbusds.jose.JOSEException: неподдерживаемый алгоритм JWS RS256, должен быть HS256, HS384 или HS512 при работе с токенами Auth0 - PullRequest
0 голосов
/ 26 апреля 2018

У нас есть приложение, которое использует пружинный отдых безопасности в качестве механизма безопасности, и мы решили переключиться на Auth0. Я пытаюсь заставить плагин Grails Spring Security Rest работать с токенами Auth0. Исходя из документации, кажется, что самый простой способ достичь этого - реализовать собственное хранилище токенов, которое будет проверять токен на соответствие Auth0

.

Прямо сейчас я просто создал свой собственный класс хранения токенов, чтобы понять, как именно это работает. Моя реализация хранилища безопасности в настоящее время точно такая же, как и в исходном JwtTokenStorage (пока я пытаюсь понять механизм):

package priz.be

import com.nimbusds.jose.JOSEException
import com.nimbusds.jwt.JWT
import grails.plugin.springsecurity.rest.JwtService
import grails.plugin.springsecurity.rest.token.storage.TokenNotFoundException
import grails.plugin.springsecurity.rest.token.storage.TokenStorageService
import grails.plugin.springsecurity.rest.token.storage.jwt.JwtTokenStorageService
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService

import java.text.ParseException

@Slf4j
@CompileStatic
class Auth0TokenStorageService extends JwtTokenStorageService {


    JwtService jwtService
    UserDetailsService userDetailsService

    @Override
    UserDetails loadUserByToken(String tokenValue) throws TokenNotFoundException {
        Date now = new Date()
        try {
            JWT jwt = jwtService.parse(tokenValue)

            if (jwt.JWTClaimsSet.expirationTime?.before(now)) {
                throw new TokenNotFoundException("Token ${tokenValue} has expired")
            }
            boolean isRefreshToken = jwt.JWTClaimsSet.expirationTime == null

            if (isRefreshToken) {
                UserDetails principal = userDetailsService.loadUserByUsername(jwt.JWTClaimsSet.subject)

                if (!principal) {
                    throw new TokenNotFoundException("Token no longer valid, principal not found")
                }
                if (!principal.enabled) {
                    throw new TokenNotFoundException("Token no longer valid, account disabled")
                }
                if (!principal.accountNonExpired) {
                    throw new TokenNotFoundException("Token no longer valid, account expired")
                }
                if (!principal.accountNonLocked) {
                    throw new TokenNotFoundException("Token no longer valid, account locked")
                }
                if (!principal.credentialsNonExpired) {
                    throw new TokenNotFoundException("Token no longer valid, credentials expired")
                }

                return principal
            }

            def roles = jwt.JWTClaimsSet.getStringArrayClaim('roles')?.collect { String role -> new SimpleGrantedAuthority(role) }

            log.debug "Successfully verified JWT"

            log.debug "Trying to deserialize the principal object"
            try {
                UserDetails details = JwtService.deserialize(jwt.JWTClaimsSet.getStringClaim('principal'))
                log.debug "UserDetails deserialized: ${details}"
                if (details) {
                    return details
                }
            } catch (exception) {
                log.debug(exception.message)
            }

            log.debug "Returning a org.springframework.security.core.userdetails.User instance"
            return new User(jwt.JWTClaimsSet.subject, 'N/A', roles)
        } catch (ParseException pe) {
            throw new TokenNotFoundException("Token ${tokenValue} is not valid")
        } catch (JOSEException je) {
            throw new TokenNotFoundException("Token ${tokenValue} has an invalid signature")
        }
    }

    @Override
    void storeToken(String tokenValue, UserDetails principal) {
        log.debug "Nothing to store as this is a stateless implementation"
    }

    @Override
    void removeToken(String tokenValue) throws TokenNotFoundException {
        log.debug "Nothing to remove as this is a stateless implementation"
        throw new TokenNotFoundException("Token ${tokenValue} cannot be removed as this is a stateless implementation")
    }
}

Если я отправляю запрос в API с действительным токеном Auth0, я получаю в качестве ответа недопустимый токен.

Копая немного глубже, я обнаружил, что, пока код пытается проверить токен, он переходит в функцию JwtService.parse, где он разрешает токен как HMAC signed (не уверен почему, но давайте предположим, что для момент правильный) здесь:

JWT parse(String tokenValue) {
        JWT jwt = JWTParser.parse(tokenValue)

        if (jwt instanceof  SignedJWT) {
            log.debug "Parsed an HMAC signed JWT"

            SignedJWT signedJwt = jwt as SignedJWT
            if(!signedJwt.verify(new MACVerifier(jwtSecret))) {
                throw new JOSEException('Invalid signature')
            }
        } else if (jwt instanceof EncryptedJWT) {
          ...

        return jwt
    }

Затем он пытается делегировать проверку на MACVerifier, однако при разрешении имени алгоритма происходит сбой, поскольку RS256 не поддерживается:

protected static String getJCAAlgorithmName(final JWSAlgorithm alg)
        throws JOSEException {

        if (alg.equals(JWSAlgorithm.HS256)) {
            return "HMACSHA256";
        } else if (alg.equals(JWSAlgorithm.HS384)) {
            return "HMACSHA384";
        } else if (alg.equals(JWSAlgorithm.HS512)) {
            return "HMACSHA512";
        } else {
            throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(
                alg,
                SUPPORTED_ALGORITHMS));
        }
    }

Я совершенно уверен, что это то, что я делаю неправильно, просто могу придумать что-нибудь на этом этапе. Что мне здесь не хватает?

...