/** * Copyright 2005-2014 Restlet * * The contents of this file are subject to the terms of one of the following * open source licenses: Apache 2.0 or LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL * 1.0 (the "Licenses"). You can select the license that you prefer but you may * not use this file except in compliance with one of these Licenses. * * You can obtain a copy of the Apache 2.0 license at * http://www.opensource.org/licenses/apache-2.0 * * You can obtain a copy of the LGPL 3.0 license at * http://www.opensource.org/licenses/lgpl-3.0 * * You can obtain a copy of the LGPL 2.1 license at * http://www.opensource.org/licenses/lgpl-2.1 * * You can obtain a copy of the CDDL 1.0 license at * http://www.opensource.org/licenses/cddl1 * * You can obtain a copy of the EPL 1.0 license at * http://www.opensource.org/licenses/eclipse-1.0 * * See the Licenses for the specific language governing permissions and * limitations under the Licenses. * * Alternatively, you can obtain a royalty free commercial license with less * limitations, transferable or non-transferable, directly at * http://www.restlet.com/products/restlet-framework * * Restlet is a registered trademark of Restlet */ package org.restlet.ext.oauth; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.deviceconnect.android.localoauth.temp.RedirectRepresentation; import org.restlet.ext.oauth.internal.Client; import org.restlet.data.Form; import org.restlet.data.Reference; import org.restlet.ext.oauth.internal.AuthSession; import org.restlet.ext.oauth.internal.RedirectionURI; import org.restlet.ext.oauth.internal.Scope; import org.restlet.ext.oauth.internal.Scopes; import org.restlet.ext.oauth.internal.ServerToken; import org.restlet.representation.Representation; import org.restlet.security.User; /** * * Restletの実装クラスAuthorizationService。OAuthの2.0許可要求を開始するために使用。 * Restlet implementation class AuthorizationService. Used for initiating an * OAuth 2.0 authorization request. * * このリソースは、コンテキスト属性パラメータにによって制御されている * This Resource is controlled by to Context Attribute Parameters * * Implements OAuth 2.0 (RFC6749) * * 次の例は、単純な認証サービスを設定する方法を示しています。 * The following example shows how to set up a simple Authorization Service. * * <pre> * { * @code * public Restlet createInboundRoot(){ * ... * ChallengeAuthenticator au = new ChallengeAuthenticator(getContext(), * ChallengeScheme.HTTP_BASIC, "OAuth Test Server"); * au.setVerifier(new MyVerifier()); * au.setNext(AuthorizationServerResource.class); * root.attach("/authorize", au); * ... * } * </pre> * ------------------------------------------------------------------------ * { * @code * public Restlet createInboundRoot(){ * ... * ChallengeAuthenticator au = new ChallengeAuthenticator(getContext(), * ChallengeScheme.HTTP_BASIC, "OAuth Test Server"); * au.setVerifier(new MyVerifier()); * au.setNext(AuthorizationServerResource.class); * root.attach("/authorize", au); * ... * } * ------------------------------------------------------------------------ * * * @author Shotaro Uchida <fantom@xmaker.mx> * @author Martin Svensson * * @see <a href="http://tools.ietf.org/html/rfc6749#section-3.1">OAuth 2.0</a> */ public class AuthorizationServerResource extends AuthorizationBaseServerResource { /* * The authorization server MUST support the use of the HTTP "GET" method * [RFC2616] for the authorization endpoint and MAY support the use of the * "POST" method as well. (3.1. Authorization Endpoint) */ public static final String PARAMETER_SUPPORT_POST = "supportPost"; /** * Checks that all incoming requests have a type parameter. Requires * response_type, client_id and redirect_uri parameters. For the code flow * client_secret is also mandatory. */ public static Representation requestAuthorization(Form params) throws OAuthException { AuthSession session = getAuthSession(); // <- Cookie[ClientCookieID]をキーにgetContext().getAttributes()の配列から取得したセッションデータ if (session != null) { return doPostAuthorization(session, // 認可要求を処理します。 clients.findById(session.getClientId())); } final Client client; final RedirectionURI redirectURI; try { client = getClient(params); // <- Form[CLIENT_ID]とclients(ClientManager)から、clientを取得 redirectURI = getRedirectionURI(params, client); // <- Form[REDIR_URI]から、redirectURIを取得。redirectURIはclient.getRedirectURIs()に存在しなければ例外 } catch (OAuthException ex) { getLogger().warning("requestAuthorization() - OAuthException() - ex.getErrorDescription():" + ex.getErrorDescription()); /* * MUST NOT automatically redirect the user-agent to the invalid * redirection URI. (see 3.1.2.4. Invalid Endpoint) */ return getErrorPage( HttpOAuthHelper.getErrorPageTemplate(getResourceContext()), ex); } catch (Exception ex) { // All other exception should be caught as server_error. OAuthException oex = new OAuthException(OAuthError.server_error, ex.getMessage(), null); getLogger().warning("requestAuthorization() - OAuthException() - ex.getMessage():" + ex.getMessage()); return getErrorPage( HttpOAuthHelper.getErrorPageTemplate(getResourceContext()), oex); } // Start Session getLogger().warning("requestAuthorization() - Start Session"); session = setupAuthSession(redirectURI); // <- セッション開始 // Setup session attributes getLogger().warning("requestAuthorization() - Setup session attributes"); try { ResponseType[] responseTypes = getResponseType(params); // <- Form[RESPONSE_TYPE]からレスポンスタイプ取得 if (responseTypes.length != 1) { // <- 1件以外ならエラー getLogger().warning("requestAuthorization() - Extension response types are not supported."); throw new OAuthException(OAuthError.unsupported_response_type, "Extension response types are not supported.", null); } if (!client.isResponseTypeAllowed(responseTypes[0])) { // <- responseTypeに認識できない値がきたらエラー getLogger().warning("requestAuthorization() - Unauthorized response type."); throw new OAuthException(OAuthError.unauthorized_client, "Unauthorized response type.", null); } session.setAuthFlow(responseTypes[0]); // <- セッションにresponseType設定 session.setClientId(client.getClientId()); // <- セッションにクライアントID設定 String[] scope = getScope(params); // <- Formからscopeを取得 session.setRequestedScope(scope); // <- セッションに要求するscope設定 String state = getState(params); // <- Formからstate値を取得 if (state != null && !state.isEmpty()) { session.setState(state); // <- state値が設定されていたらセッションに設定 } } catch (OAuthException ex) { getLogger().warning("requestAuthorization() - OAuthException message:" + ex.getMessage() + " description:" + ex.getErrorDescription()); ungetAuthSession(); throw ex; } User scopeOwner = getRequest().getClientInfo().getUser(); if (scopeOwner != null) { // If user information is present, use as scope owner. session.setScopeOwner(scopeOwner.getIdentifier()); // セッションにownerを設定(ユーザーIDを保存する) } getLogger().warning("requestAuthorization() - session.getScopeOwner():" + session.getScopeOwner()); if (session.getScopeOwner() == null) { // "owner"が設定されなかった場合 // Redirect to login page. getLogger().warning("requestAuthorization() - redirectTemporary()"); Map<String, Object> options = new HashMap<String, Object>(); options.put(RedirectRepresentation.SESSION_ID, session.getId()); return new RedirectRepresentation(RedirectRepresentation.RedirectProc.loginPage, options); } getLogger().warning("requestAuthorization() - doPostAuthorization()"); return doPostAuthorization(session, client); // 認可要求を処理します。 } /** * 認可要求を処理します。<br> * Handle the authorization request. * * @param session * The OAuth session. * * @return The result as a {@link Representation}. */ protected static Representation doPostAuthorization(AuthSession session, Client client) { // URL生成 : "riap://application" + AuthPage + "client=<clientid>" + "scope=<scope>&scope=...(scopeの数分)" + "grantedScope=<grandScope>&grantedScope=...(grandScopeの数分)" Reference ref = new Reference("riap://application" + HttpOAuthHelper.getAuthPage(getResourceContext())); ref.addQueryParameter("client", session.getClientId()); // Requested scope should not be null. String[] scopes = session.getRequestedScope(); for (String s : scopes) { ref.addQueryParameter("scope", s); // sessionに格納されていたRequestedScopeをURLに追加する。"scope" } ServerToken token = (ServerToken) tokens.findToken(client, // session.getScopeOwner() のtokenをTokenManagerの中から検索 session.getScopeOwner()); if (token != null && !token.isExpired()) { for (Scope s : token.getScope()) { ref.addQueryParameter("grantedScope", s.toString()); // tokenが存在すれば、URLに追加する。"grantedScope" } } Map<String, Object> options = new HashMap<String, Object>(); ArrayList<String> scopes2 = new ArrayList<String>(); for (String s : scopes) { scopes2.add(s); } ArrayList<String> grantedScopes = new ArrayList<String>(); if (token != null && !token.isExpired()) { for (Scope s : token.getScope()) { grantedScopes.add(s.toString()); } } options.put("scope", scopes2); options.put("grantedScope", grantedScopes); return new RedirectRepresentation(RedirectRepresentation.RedirectProc.authPage, options); } /** * Get request parameter "response_type". * * @param params * @return * @throws OAuthException */ protected static ResponseType[] getResponseType(Form params) throws OAuthException { String responseType = params.getFirstValue(RESPONSE_TYPE); if (responseType == null || responseType.isEmpty()) { throw new OAuthException(OAuthError.invalid_request, "No response_type parameter found.", null); } /* * Extension response types MAY contain a space-delimited (%x20) list of * values (3.1.1. Response Type) */ String[] typesString = Scopes.parseScope(responseType); // The same // format as // scope. ResponseType[] types = new ResponseType[typesString.length]; for (int i = 0; i < typesString.length; i++) { try { ResponseType type = Enum.valueOf(ResponseType.class, typesString[i]); getLogger().fine("Found flow - " + type); types[i] = type; } catch (IllegalArgumentException iae) { throw new OAuthException(OAuthError.unsupported_response_type, "Unsupported flow", null); } } return types; } /** * Get request parameter "redirect_uri". (See 3.1.2.3. Dynamic * Configuration) * * @param params * @param client * @return * @throws OAuthException */ protected static RedirectionURI getRedirectionURI(Form params, Client client) throws OAuthException { String redirectURI = params.getFirstValue(REDIR_URI); String[] redirectURIs = client.getRedirectURIs(); /* * If multiple redirection URIs have been registered, if only part of * the redirection URI has been registered, or if no redirection URI has * been registered, the client MUST include a redirection URI with the * authorization request using the "redirect_uri" request parameter. * (See 3.1.2.3. Dynamic Configuration) */ if (redirectURIs == null || redirectURIs.length != 1) { if (redirectURI == null || redirectURI.isEmpty()) { throw new OAuthException(OAuthError.invalid_request, "Client MUST include a redirection URI.", null); } } else { if (redirectURI == null || redirectURI.isEmpty()) { // If the optional parameter redirect_uri is not provided, // we use the one provided during client registration. return new RedirectionURI(redirectURIs[0]); } } /* リダイレクトURIは、許可要求に含まれている場合、 認可サーバーは受信した値を比較し、一致しなければならない 少なくとも登録リダイレクトURIの1(またはURIに対して [RFC3986]セクション6で定義されているコンポーネント)、もしあれば、リダイレクト URIは、登録された。 (See 3.1.2.3. Dynamic Configuration) * When a redirection URI is included in an authorization request, the * authorization server MUST compare and match the value received * against at least one of the registered redirection URIs (or URI * components) as defined in [RFC3986] Section 6, if any redirection * URIs were registered. (See 3.1.2.3. Dynamic Configuration) */ for (String uri : redirectURIs) { if (redirectURI.startsWith(uri)) { return new RedirectionURI(redirectURI, true); } } // The provided uri is no based on the uri with the client registration. throw new OAuthException(OAuthError.invalid_request, "Callback URI does not match.", null); } }