001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * 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 distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.device;
019
020
021import com.nimbusds.oauth2.sdk.AuthorizationGrant;
022import com.nimbusds.oauth2.sdk.GrantType;
023import com.nimbusds.oauth2.sdk.OAuth2Error;
024import com.nimbusds.oauth2.sdk.ParseException;
025import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
026import net.jcip.annotations.Immutable;
027
028import java.util.*;
029
030
031/**
032 * Device code grant for the OAuth 2.0 Device Authorization Grant.
033 *
034 * <p>Related specifications:
035 *
036 * <ul>
037 *     <li>OAuth 2.0 Device Authorization Grant (RFC 8628)
038 * </ul>
039 */
040@Immutable
041public class DeviceCodeGrant extends AuthorizationGrant {
042
043        
044        /**
045         * The grant type.
046         */
047        public static final GrantType GRANT_TYPE = GrantType.DEVICE_CODE;
048
049
050        /**
051         * The device code received from the authorisation server.
052         */
053        private final DeviceCode deviceCode;
054
055
056        /**
057         * Creates a new device code grant.
058         * 
059         * @param deviceCode The device code. Must not be {@code null}.
060         */
061        public DeviceCodeGrant(final DeviceCode deviceCode) {
062
063                super(GRANT_TYPE);
064                this.deviceCode = Objects.requireNonNull(deviceCode);
065        }
066
067
068        /**
069         * Returns the device code received from the authorisation server.
070         * 
071         * @return The device code received from the authorisation server.
072         */
073        public DeviceCode getDeviceCode() {
074
075                return deviceCode;
076        }
077
078
079        @Override
080        public Map<String, List<String>> toParameters() {
081
082                Map<String, List<String>> params = new LinkedHashMap<>();
083                params.put("grant_type", Collections.singletonList(GRANT_TYPE.getValue()));
084                params.put("device_code", Collections.singletonList(deviceCode.getValue()));
085                return params;
086        }
087
088
089        /**
090         * Parses a device code grant from the specified request body
091         * parameters.
092         *
093         * <p>Example:
094         *
095         * <pre>
096         * grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code
097         * &amp;device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
098         * </pre>
099         *
100         * @param params The parameters.
101         *
102         * @return The device code grant.
103         *
104         * @throws ParseException If parsing failed.
105         */
106        public static DeviceCodeGrant parse(final Map<String, List<String>> params) throws ParseException {
107                
108                GrantType.ensure(GRANT_TYPE, params);
109                
110                String deviceCodeString = MultivaluedMapUtils.getFirstValue(params, "device_code");
111
112                if (deviceCodeString == null || deviceCodeString.trim().isEmpty()) {
113                        String msg = "Missing or empty device_code parameter";
114                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
115                }
116
117                DeviceCode deviceCode = new DeviceCode(deviceCodeString);
118
119                return new DeviceCodeGrant(deviceCode);
120        }
121
122
123        @Override
124        public boolean equals(Object o) {
125
126                if (this == o)
127                        return true;
128                if (!(o instanceof DeviceCodeGrant))
129                        return false;
130
131                DeviceCodeGrant deviceCodeGrant = (DeviceCodeGrant) o;
132                return deviceCode.equals(deviceCodeGrant.deviceCode);
133        }
134
135
136        @Override
137        public int hashCode() {
138
139                return deviceCode.hashCode();
140        }
141}