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}