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 java.util.Collections;
022import java.util.LinkedHashMap;
023import java.util.List;
024import java.util.Map;
025
026import net.jcip.annotations.Immutable;
027
028import com.nimbusds.oauth2.sdk.AuthorizationGrant;
029import com.nimbusds.oauth2.sdk.GrantType;
030import com.nimbusds.oauth2.sdk.OAuth2Error;
031import com.nimbusds.oauth2.sdk.ParseException;
032import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
033
034
035/**
036 * Device code grant for the OAuth 2.0 Device Authorization Grant.
037 *
038 * <p>Related specifications:
039 *
040 * <ul>
041 *     <li>OAuth 2.0 Device Authorization Grant (RFC 8628)
042 * </ul>
043 */
044@Immutable
045public class DeviceCodeGrant extends AuthorizationGrant {
046
047        
048        /**
049         * The grant type.
050         */
051        public static final GrantType GRANT_TYPE = GrantType.DEVICE_CODE;
052
053
054        /**
055         * The device code received from the authorisation server.
056         */
057        private final DeviceCode deviceCode;
058
059
060        /**
061         * Creates a new device code grant.
062         * 
063         * @param deviceCode The device code. Must not be {@code null}.
064         */
065        public DeviceCodeGrant(final DeviceCode deviceCode) {
066
067                super(GRANT_TYPE);
068
069                if (deviceCode == null)
070                        throw new IllegalArgumentException("The device code must not be null");
071
072                this.deviceCode = deviceCode;
073        }
074
075
076        /**
077         * Returns the device code received from the authorisation server.
078         * 
079         * @return The device code received from the authorisation server.
080         */
081        public DeviceCode getDeviceCode() {
082
083                return deviceCode;
084        }
085
086
087        @Override
088        public Map<String, List<String>> toParameters() {
089
090                Map<String, List<String>> params = new LinkedHashMap<>();
091                params.put("grant_type", Collections.singletonList(GRANT_TYPE.getValue()));
092                params.put("device_code", Collections.singletonList(deviceCode.getValue()));
093                return params;
094        }
095
096
097        /**
098         * Parses a device code grant from the specified request body
099         * parameters.
100         *
101         * <p>Example:
102         *
103         * <pre>
104         * grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code
105         * &amp;device_code=GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS
106         * </pre>
107         *
108         * @param params The parameters.
109         *
110         * @return The device code grant.
111         *
112         * @throws ParseException If parsing failed.
113         */
114        public static DeviceCodeGrant parse(final Map<String, List<String>> params) throws ParseException {
115                
116                GrantType.ensure(GRANT_TYPE, params);
117                
118                String deviceCodeString = MultivaluedMapUtils.getFirstValue(params, "device_code");
119
120                if (deviceCodeString == null || deviceCodeString.trim().isEmpty()) {
121                        String msg = "Missing or empty device_code parameter";
122                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
123                }
124
125                DeviceCode deviceCode = new DeviceCode(deviceCodeString);
126
127                return new DeviceCodeGrant(deviceCode);
128        }
129
130
131        @Override
132        public boolean equals(Object o) {
133
134                if (this == o)
135                        return true;
136                if (!(o instanceof DeviceCodeGrant))
137                        return false;
138
139                DeviceCodeGrant deviceCodeGrant = (DeviceCodeGrant) o;
140                return deviceCode.equals(deviceCodeGrant.deviceCode);
141        }
142
143
144        @Override
145        public int hashCode() {
146
147                return deviceCode.hashCode();
148        }
149}