001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2014 ForgeRock AS. 015 */ 016package org.forgerock.openig.filter.oauth2.client; 017 018import static org.forgerock.openig.filter.oauth2.client.OAuth2Utils.*; 019import static org.forgerock.openig.util.Json.*; 020 021import java.net.URI; 022import java.nio.charset.Charset; 023import java.util.Collections; 024import java.util.List; 025 026import org.forgerock.json.fluent.JsonValue; 027import org.forgerock.openig.el.Expression; 028import org.forgerock.openig.handler.HandlerException; 029import org.forgerock.openig.http.Exchange; 030import org.forgerock.openig.http.Form; 031import org.forgerock.openig.http.Request; 032import org.forgerock.util.encode.Base64; 033 034/** 035 * An OAuth 2.0 authorization server or OpenID Connect Provider. 036 */ 037public class OAuth2Provider { 038 private final String name; 039 private Expression authorizeEndpoint; 040 private Expression clientId; 041 private Expression clientSecret; 042 private List<Expression> scopes; 043 private Expression tokenEndpoint; 044 private Expression userInfoEndpoint; 045 private final boolean tokenEndpointUseBasicAuth = false; // Do we want to make this configurable? 046 047 /** 048 * Creates a new provider having the specified name. The returned provider 049 * should be configured using the setters before being used. 050 * 051 * @param name 052 * The provider name. 053 */ 054 public OAuth2Provider(final String name) { 055 this.name = name; 056 } 057 058 /** 059 * Sets the expression which will be used for obtaining the authorization 060 * server's authorize end-point. This configuration parameter is required. 061 * 062 * @param endpoint 063 * The expression which will be used for obtaining the 064 * authorization server's authorize end-point. 065 * @return This provider. 066 */ 067 public OAuth2Provider setAuthorizeEndpoint(final Expression endpoint) { 068 this.authorizeEndpoint = endpoint; 069 return this; 070 } 071 072 /** 073 * Sets the expression which will be used for obtaining the OAuth 2 client 074 * ID. This configuration parameter is required. 075 * 076 * @param clientId 077 * The expression which will be used for obtaining the OAuth 2 078 * client ID. 079 * @return This provider. 080 */ 081 public OAuth2Provider setClientId(final Expression clientId) { 082 this.clientId = clientId; 083 return this; 084 } 085 086 /** 087 * Sets the expression which will be used for obtaining the OAuth 2 client 088 * secret. This configuration parameter is required. 089 * 090 * @param clientSecret 091 * The expression which will be used for obtaining the OAuth 2 092 * client secret. 093 * @return This provider. 094 */ 095 public OAuth2Provider setClientSecret(final Expression clientSecret) { 096 this.clientSecret = clientSecret; 097 return this; 098 } 099 100 /** 101 * Sets the expressions which will be used for obtaining the OAuth 2 scopes 102 * for this provider. This configuration parameter is optional and defaults 103 * to the set of scopes defined for the client filter. 104 * 105 * @param scopes 106 * The expressions which will be used for obtaining the OAuth 2 107 * scopes. 108 * @return This provider. 109 */ 110 public OAuth2Provider setScopes(final List<Expression> scopes) { 111 this.scopes = scopes != null ? scopes : Collections.<Expression> emptyList(); 112 return this; 113 } 114 115 /** 116 * Sets the expression which will be used for obtaining the authorization 117 * server's access token end-point. This configuration parameter is 118 * required. 119 * 120 * @param endpoint 121 * The expression which will be used for obtaining the 122 * authorization server's access token end-point. 123 * @return This provider. 124 */ 125 public OAuth2Provider setTokenEndpoint(final Expression endpoint) { 126 this.tokenEndpoint = endpoint; 127 return this; 128 } 129 130 /** 131 * Sets the expression which will be used for obtaining the authorization 132 * server's OpenID Connect user info end-point. This configuration parameter 133 * is optional. 134 * 135 * @param endpoint 136 * The expression which will be used for obtaining the 137 * authorization server's OpenID Connect user info end-point. 138 * @return This provider. 139 */ 140 public OAuth2Provider setUserInfoEndpoint(final Expression endpoint) { 141 this.userInfoEndpoint = endpoint; 142 return this; 143 } 144 145 /** 146 * Configures this provider using the specified OpenID Connect Well Known 147 * configuration. 148 * 149 * @param wellKnown 150 * The OpenID Connect provider's Well Known configuration. 151 * @return This provider. 152 */ 153 public OAuth2Provider setWellKnownConfiguration(final JsonValue wellKnown) { 154 setAuthorizeEndpoint(asExpression(wellKnown.get("authorization_endpoint").required())); 155 setTokenEndpoint(asExpression(wellKnown.get("token_endpoint").required())); 156 setUserInfoEndpoint(asExpression(wellKnown.get("userinfo_endpoint"))); 157 return this; 158 } 159 160 Request createRequestForAccessToken(final Exchange exchange, final String code, 161 final String callbackUri) throws HandlerException { 162 final Request request = new Request(); 163 request.setMethod("POST"); 164 request.setUri(buildUri(exchange, tokenEndpoint)); 165 final Form form = new Form(); 166 form.add("grant_type", "authorization_code"); 167 form.add("redirect_uri", callbackUri); 168 form.add("code", code); 169 addClientIdAndSecret(exchange, request, form); 170 form.toRequestEntity(request); 171 return request; 172 } 173 174 Request createRequestForTokenRefresh(final Exchange exchange, final OAuth2Session session) 175 throws HandlerException { 176 final Request request = new Request(); 177 request.setMethod("POST"); 178 request.setUri(buildUri(exchange, tokenEndpoint)); 179 final Form form = new Form(); 180 form.add("grant_type", "refresh_token"); 181 form.add("refresh_token", session.getRefreshToken()); 182 addClientIdAndSecret(exchange, request, form); 183 form.toRequestEntity(request); 184 return request; 185 } 186 187 Request createRequestForUserInfo(final Exchange exchange, final String accessToken) 188 throws HandlerException { 189 final Request request = new Request(); 190 request.setMethod("GET"); 191 request.setUri(buildUri(exchange, userInfoEndpoint)); 192 request.getHeaders().add("Authorization", "Bearer " + accessToken); 193 return request; 194 } 195 196 URI getAuthorizeEndpoint(final Exchange exchange) throws HandlerException { 197 return buildUri(exchange, authorizeEndpoint); 198 } 199 200 String getClientId(final Exchange exchange) throws HandlerException { 201 final String result = clientId.eval(exchange, String.class); 202 if (result == null) { 203 throw new HandlerException("Unable to determine the clientId"); 204 } 205 return result; 206 } 207 208 String getName() { 209 return name; 210 } 211 212 List<String> getScopes(final Exchange exchange) throws HandlerException { 213 return OAuth2Utils.getScopes(exchange, scopes); 214 } 215 216 boolean hasUserInfoEndpoint() { 217 return userInfoEndpoint != null; 218 } 219 220 private void addClientIdAndSecret(final Exchange exchange, final Request request, 221 final Form form) throws HandlerException { 222 final String user = getClientId(exchange); 223 final String pass = getClientSecret(exchange); 224 if (!tokenEndpointUseBasicAuth) { 225 form.add("client_id", user); 226 form.add("client_secret", pass); 227 } else { 228 final String userpass = 229 Base64.encode((user + ":" + pass).getBytes(Charset.defaultCharset())); 230 request.getHeaders().add("Authorization", "Basic " + userpass); 231 } 232 } 233 234 private String getClientSecret(final Exchange exchange) throws HandlerException { 235 final String result = clientSecret.eval(exchange, String.class); 236 if (result == null) { 237 throw new HandlerException("Unable to determine the clientSecret"); 238 } 239 return result; 240 } 241}