/** * Copyright © 2016-2017 The Thingsboard Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.thingsboard.server.service.security.model.token; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.config.JwtSettings; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; import java.util.Arrays; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @Component public class JwtTokenFactory { private static final String SCOPES = "scopes"; private static final String USER_ID = "userId"; private static final String FIRST_NAME = "firstName"; private static final String LAST_NAME = "lastName"; private static final String ENABLED = "enabled"; private static final String IS_PUBLIC = "isPublic"; private static final String TENANT_ID = "tenantId"; private static final String CUSTOMER_ID = "customerId"; private final JwtSettings settings; @Autowired public JwtTokenFactory(JwtSettings settings) { this.settings = settings; } /** * Factory method for issuing new JWT Tokens. */ public AccessJwtToken createAccessJwtToken(SecurityUser securityUser) { if (StringUtils.isBlank(securityUser.getEmail())) throw new IllegalArgumentException("Cannot create JWT Token without username/email"); if (securityUser.getAuthority() == null) throw new IllegalArgumentException("User doesn't have any privileges"); UserPrincipal principal = securityUser.getUserPrincipal(); String subject = principal.getValue(); Claims claims = Jwts.claims().setSubject(subject); claims.put(SCOPES, securityUser.getAuthorities().stream().map(s -> s.getAuthority()).collect(Collectors.toList())); claims.put(USER_ID, securityUser.getId().getId().toString()); claims.put(FIRST_NAME, securityUser.getFirstName()); claims.put(LAST_NAME, securityUser.getLastName()); claims.put(ENABLED, securityUser.isEnabled()); claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID); if (securityUser.getTenantId() != null) { claims.put(TENANT_ID, securityUser.getTenantId().getId().toString()); } if (securityUser.getCustomerId() != null) { claims.put(CUSTOMER_ID, securityUser.getCustomerId().getId().toString()); } DateTime currentTime = new DateTime(); String token = Jwts.builder() .setClaims(claims) .setIssuer(settings.getTokenIssuer()) .setIssuedAt(currentTime.toDate()) .setExpiration(currentTime.plusSeconds(settings.getTokenExpirationTime()).toDate()) .signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey()) .compact(); return new AccessJwtToken(token, claims); } public SecurityUser parseAccessJwtToken(RawAccessJwtToken rawAccessToken) { Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey()); Claims claims = jwsClaims.getBody(); String subject = claims.getSubject(); List<String> scopes = claims.get(SCOPES, List.class); if (scopes == null || scopes.isEmpty()) { throw new IllegalArgumentException("JWT Token doesn't have any scopes"); } SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class)))); securityUser.setEmail(subject); securityUser.setAuthority(Authority.parse(scopes.get(0))); securityUser.setFirstName(claims.get(FIRST_NAME, String.class)); securityUser.setLastName(claims.get(LAST_NAME, String.class)); securityUser.setEnabled(claims.get(ENABLED, Boolean.class)); boolean isPublic = claims.get(IS_PUBLIC, Boolean.class); UserPrincipal principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject); securityUser.setUserPrincipal(principal); String tenantId = claims.get(TENANT_ID, String.class); if (tenantId != null) { securityUser.setTenantId(new TenantId(UUID.fromString(tenantId))); } String customerId = claims.get(CUSTOMER_ID, String.class); if (customerId != null) { securityUser.setCustomerId(new CustomerId(UUID.fromString(customerId))); } return securityUser; } public JwtToken createRefreshToken(SecurityUser securityUser) { if (StringUtils.isBlank(securityUser.getEmail())) { throw new IllegalArgumentException("Cannot create JWT Token without username/email"); } DateTime currentTime = new DateTime(); UserPrincipal principal = securityUser.getUserPrincipal(); Claims claims = Jwts.claims().setSubject(principal.getValue()); claims.put(SCOPES, Arrays.asList(Authority.REFRESH_TOKEN.name())); claims.put(USER_ID, securityUser.getId().getId().toString()); claims.put(IS_PUBLIC, principal.getType() == UserPrincipal.Type.PUBLIC_ID); String token = Jwts.builder() .setClaims(claims) .setIssuer(settings.getTokenIssuer()) .setId(UUID.randomUUID().toString()) .setIssuedAt(currentTime.toDate()) .setExpiration(currentTime.plusSeconds(settings.getRefreshTokenExpTime()).toDate()) .signWith(SignatureAlgorithm.HS512, settings.getTokenSigningKey()) .compact(); return new AccessJwtToken(token, claims); } public SecurityUser parseRefreshToken(RawAccessJwtToken rawAccessToken) { Jws<Claims> jwsClaims = rawAccessToken.parseClaims(settings.getTokenSigningKey()); Claims claims = jwsClaims.getBody(); String subject = claims.getSubject(); List<String> scopes = claims.get(SCOPES, List.class); if (scopes == null || scopes.isEmpty()) { throw new IllegalArgumentException("Refresh Token doesn't have any scopes"); } if (!scopes.get(0).equals(Authority.REFRESH_TOKEN.name())) { throw new IllegalArgumentException("Invalid Refresh Token scope"); } boolean isPublic = claims.get(IS_PUBLIC, Boolean.class); UserPrincipal principal = new UserPrincipal(isPublic ? UserPrincipal.Type.PUBLIC_ID : UserPrincipal.Type.USER_NAME, subject); SecurityUser securityUser = new SecurityUser(new UserId(UUID.fromString(claims.get(USER_ID, String.class)))); securityUser.setUserPrincipal(principal); return securityUser; } }