001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 *******************************************************************************/ 016/** 017 * 018 */ 019package org.mitre.openid.connect.view; 020 021import java.io.IOException; 022import java.io.StringWriter; 023import java.io.Writer; 024import java.text.ParseException; 025import java.util.Date; 026import java.util.Map; 027import java.util.UUID; 028 029import javax.servlet.http.HttpServletRequest; 030import javax.servlet.http.HttpServletResponse; 031 032import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService; 033import org.mitre.jwt.signer.service.JWTSigningAndValidationService; 034import org.mitre.jwt.signer.service.impl.ClientKeyCacheService; 035import org.mitre.jwt.signer.service.impl.SymmetricKeyJWTValidatorCacheService; 036import org.mitre.oauth2.model.ClientDetailsEntity; 037import org.mitre.openid.connect.config.ConfigurationPropertiesBean; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040import org.springframework.beans.factory.annotation.Autowired; 041import org.springframework.http.MediaType; 042import org.springframework.stereotype.Component; 043 044import com.google.common.base.Strings; 045import com.google.common.collect.Lists; 046import com.google.gson.JsonObject; 047import com.nimbusds.jose.Algorithm; 048import com.nimbusds.jose.JWEHeader; 049import com.nimbusds.jose.JWSAlgorithm; 050import com.nimbusds.jose.JWSHeader; 051import com.nimbusds.jwt.EncryptedJWT; 052import com.nimbusds.jwt.JWTClaimsSet; 053import com.nimbusds.jwt.SignedJWT; 054 055/** 056 * @author jricher 057 * 058 */ 059@Component(UserInfoJWTView.VIEWNAME) 060public class UserInfoJWTView extends UserInfoView { 061 062 public static final String CLIENT = "client"; 063 064 /** 065 * Logger for this class 066 */ 067 private static final Logger logger = LoggerFactory.getLogger(UserInfoJWTView.class); 068 069 public static final String VIEWNAME = "userInfoJwtView"; 070 071 public static final String JOSE_MEDIA_TYPE_VALUE = "application/jwt"; 072 public static final MediaType JOSE_MEDIA_TYPE = new MediaType("application", "jwt"); 073 074 075 @Autowired 076 private JWTSigningAndValidationService jwtService; 077 078 @Autowired 079 private ConfigurationPropertiesBean config; 080 081 @Autowired 082 private ClientKeyCacheService encrypters; 083 084 @Autowired 085 private SymmetricKeyJWTValidatorCacheService symmetricCacheService; 086 087 @Override 088 protected void writeOut(JsonObject json, Map<String, Object> model, 089 HttpServletRequest request, HttpServletResponse response) { 090 091 try { 092 ClientDetailsEntity client = (ClientDetailsEntity)model.get(CLIENT); 093 094 // use the parser to import the user claims into the object 095 StringWriter writer = new StringWriter(); 096 gson.toJson(json, writer); 097 098 response.setContentType(JOSE_MEDIA_TYPE_VALUE); 099 100 JWTClaimsSet claims = new JWTClaimsSet.Builder(JWTClaimsSet.parse(writer.toString())) 101 .audience(Lists.newArrayList(client.getClientId())) 102 .issuer(config.getIssuer()) 103 .issueTime(new Date()) 104 .jwtID(UUID.randomUUID().toString()) // set a random NONCE in the middle of it 105 .build(); 106 107 108 if (client.getUserInfoEncryptedResponseAlg() != null && !client.getUserInfoEncryptedResponseAlg().equals(Algorithm.NONE) 109 && client.getUserInfoEncryptedResponseEnc() != null && !client.getUserInfoEncryptedResponseEnc().equals(Algorithm.NONE) 110 && (!Strings.isNullOrEmpty(client.getJwksUri()) || client.getJwks() != null)) { 111 112 // encrypt it to the client's key 113 114 JWTEncryptionAndDecryptionService encrypter = encrypters.getEncrypter(client); 115 116 if (encrypter != null) { 117 118 EncryptedJWT encrypted = new EncryptedJWT(new JWEHeader(client.getUserInfoEncryptedResponseAlg(), client.getUserInfoEncryptedResponseEnc()), claims); 119 120 encrypter.encryptJwt(encrypted); 121 122 123 Writer out = response.getWriter(); 124 out.write(encrypted.serialize()); 125 126 } else { 127 logger.error("Couldn't find encrypter for client: " + client.getClientId()); 128 } 129 } else { 130 131 JWSAlgorithm signingAlg = jwtService.getDefaultSigningAlgorithm(); // default to the server's preference 132 if (client.getUserInfoSignedResponseAlg() != null) { 133 signingAlg = client.getUserInfoSignedResponseAlg(); // override with the client's preference if available 134 } 135 JWSHeader header = new JWSHeader(signingAlg, null, null, null, null, null, null, null, null, null, 136 jwtService.getDefaultSignerKeyId(), 137 null, null); 138 SignedJWT signed = new SignedJWT(header, claims); 139 140 if (signingAlg.equals(JWSAlgorithm.HS256) 141 || signingAlg.equals(JWSAlgorithm.HS384) 142 || signingAlg.equals(JWSAlgorithm.HS512)) { 143 144 // sign it with the client's secret 145 JWTSigningAndValidationService signer = symmetricCacheService.getSymmetricValidtor(client); 146 signer.signJwt(signed); 147 148 } else { 149 // sign it with the server's key 150 jwtService.signJwt(signed); 151 } 152 153 Writer out = response.getWriter(); 154 out.write(signed.serialize()); 155 } 156 } catch (IOException e) { 157 logger.error("IO Exception in UserInfoJwtView", e); 158 } catch (ParseException e) { 159 // TODO Auto-generated catch block 160 e.printStackTrace(); 161 } 162 163 } 164}