001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 003 * 004 * Portions copyright 2011-2013 The MITRE Corporation 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 *******************************************************************************/ 018package org.mitre.openid.connect.client; 019 020import java.util.Collection; 021 022import org.mitre.openid.connect.model.OIDCAuthenticationToken; 023import org.mitre.openid.connect.model.PendingOIDCAuthenticationToken; 024import org.mitre.openid.connect.model.UserInfo; 025import org.slf4j.Logger; 026import org.slf4j.LoggerFactory; 027import org.springframework.security.authentication.AuthenticationProvider; 028import org.springframework.security.core.Authentication; 029import org.springframework.security.core.AuthenticationException; 030import org.springframework.security.core.GrantedAuthority; 031import org.springframework.security.core.userdetails.UsernameNotFoundException; 032 033import com.google.common.base.Strings; 034import com.nimbusds.jwt.JWT; 035 036/** 037 * @author nemonik, Justin Richer 038 * 039 */ 040public class OIDCAuthenticationProvider implements AuthenticationProvider { 041 042 private static Logger logger = LoggerFactory.getLogger(OIDCAuthenticationProvider.class); 043 044 private UserInfoFetcher userInfoFetcher = new UserInfoFetcher(); 045 046 private OIDCAuthoritiesMapper authoritiesMapper = new NamedAdminAuthoritiesMapper(); 047 048 /* 049 * (non-Javadoc) 050 * 051 * @see org.springframework.security.authentication.AuthenticationProvider# 052 * authenticate(org.springframework.security.core.Authentication) 053 */ 054 @Override 055 public Authentication authenticate(final Authentication authentication) throws AuthenticationException { 056 057 if (!supports(authentication.getClass())) { 058 return null; 059 } 060 061 if (authentication instanceof PendingOIDCAuthenticationToken) { 062 063 PendingOIDCAuthenticationToken token = (PendingOIDCAuthenticationToken) authentication; 064 065 // get the ID Token value out 066 JWT idToken = token.getIdToken(); 067 068 // load the user info if we can 069 UserInfo userInfo = userInfoFetcher.loadUserInfo(token); 070 071 if (userInfo == null) { 072 // user info not found -- could be an error, could be fine 073 } else { 074 // if we found userinfo, double check it 075 if (!Strings.isNullOrEmpty(userInfo.getSub()) && !userInfo.getSub().equals(token.getSub())) { 076 // the userinfo came back and the user_id fields don't match what was in the id_token 077 throw new UsernameNotFoundException("user_id mismatch between id_token and user_info call: " + token.getSub() + " / " + userInfo.getSub()); 078 } 079 } 080 081 return createAuthenticationToken(token, authoritiesMapper.mapAuthorities(idToken, userInfo), userInfo); 082 } 083 084 return null; 085 } 086 087 /** 088 * Override this function to return a different kind of Authentication, processes the authorities differently, 089 * or do post-processing based on the UserInfo object. 090 * 091 * @param token 092 * @param authorities 093 * @param userInfo 094 * @return 095 */ 096 protected Authentication createAuthenticationToken(PendingOIDCAuthenticationToken token, Collection<? extends GrantedAuthority> authorities, UserInfo userInfo) { 097 return new OIDCAuthenticationToken(token.getSub(), 098 token.getIssuer(), 099 userInfo, authorities, 100 token.getIdToken(), token.getAccessTokenValue(), token.getRefreshTokenValue()); 101 } 102 103 /** 104 * @param userInfoFetcher 105 */ 106 public void setUserInfoFetcher(UserInfoFetcher userInfoFetcher) { 107 this.userInfoFetcher = userInfoFetcher; 108 } 109 110 /** 111 * @param authoritiesMapper 112 */ 113 public void setAuthoritiesMapper(OIDCAuthoritiesMapper authoritiesMapper) { 114 this.authoritiesMapper = authoritiesMapper; 115 } 116 117 /* 118 * (non-Javadoc) 119 * 120 * @see 121 * org.springframework.security.authentication.AuthenticationProvider#supports 122 * (java.lang.Class) 123 */ 124 @Override 125 public boolean supports(Class<?> authentication) { 126 return PendingOIDCAuthenticationToken.class.isAssignableFrom(authentication); 127 } 128}