001package com.nimbusds.oauth2.sdk;
002
003
004import java.util.*;
005
006import net.jcip.annotations.Immutable;
007
008import com.nimbusds.jose.*;
009import com.nimbusds.jwt.*;
010
011
012/**
013 * JWT bearer grant. Used in access token requests with a JSON Web Token (JWT),
014 * such an OpenID Connect ID token.
015 *
016 * <p>Related specifications:
017 *
018 * <ul>
019 *     <li>Assertion Framework for OAuth 2.0 Client Authentication and
020 *         Authorization Grants (RFC 7521), section 4.1.
021 *     <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and
022 *         Authorization Grants (RFC 7523), section-2.1.
023 * </ul>
024 */
025@Immutable
026public class JWTBearerGrant extends AssertionGrant {
027
028
029        /**
030         * The grant type.
031         */
032        public static final GrantType GRANT_TYPE = GrantType.JWT_BEARER;
033
034
035        /**
036         * The JWT assertion.
037         */
038        private final JWT assertion;
039
040
041        /**
042         * Creates a new signed JSON Web Token (JWT) bearer assertion grant.
043         *
044         * @param assertion The signed JSON Web Token (JWT) assertion. Must not
045         *                  be in a unsigned state or {@code null}. The JWT
046         *                  claims are not validated for compliance with the
047         *                  standard.
048         */
049        public JWTBearerGrant(final SignedJWT assertion) {
050
051                super(GRANT_TYPE);
052
053                if (assertion.getState().equals(JWSObject.State.UNSIGNED))
054                        throw new IllegalArgumentException("The JWT assertion must not be in a unsigned state");
055
056                this.assertion = assertion;
057        }
058
059
060        /**
061         * Creates a new signed and encrypted JSON Web Token (JWT) bearer
062         * assertion grant.
063         *
064         * @param assertion The signed and encrypted JSON Web Token (JWT)
065         *                  assertion. Must not be in a unencrypted state or
066         *                  {@code null}. The JWT claims are not validated for
067         *                  compliance with the standard.
068         */
069        public JWTBearerGrant(final EncryptedJWT assertion) {
070
071                super(GRANT_TYPE);
072
073                if (assertion.getState().equals(JWEObject.State.UNENCRYPTED))
074                        throw new IllegalArgumentException("The JWT assertion must not be in a unencrypted state");
075
076                this.assertion = assertion;
077        }
078
079
080        /**
081         * Gets the JSON Web Token (JWT) bearer assertion.
082         *
083         * @return The signed or signed and encrypted JWT bearer assertion.
084         */
085        public JWT getJWTAssertion() {
086
087                return assertion;
088        }
089
090
091        @Override
092        public String getAssertion() {
093
094                return assertion.serialize();
095        }
096
097
098        @Override
099        public Map<String,String> toParameters() {
100
101                Map<String,String> params = new LinkedHashMap<>();
102                params.put("grant_type", GRANT_TYPE.getValue());
103                params.put("assertion", assertion.serialize());
104                return params;
105        }
106
107
108        /**
109         * Parses a JWT bearer grant from the specified parameters. The JWT
110         * claims are not validated for compliance with the standard.
111         *
112         * <p>Example:
113         *
114         * <pre>
115         * grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
116         * &assertion=eyJhbGciOiJFUzI1NiJ9.eyJpc3Mi[...omitted for brevity...].
117         * J9l-ZhwP[...omitted for brevity...]
118         * </pre>
119         *
120         * @param params The parameters.
121         *
122         * @return The JWT bearer grant.
123         *
124         * @throws ParseException If parsing failed.
125         */
126        public static JWTBearerGrant parse(final Map<String,String> params)
127                throws ParseException {
128
129                // Parse grant type
130                String grantTypeString = params.get("grant_type");
131
132                if (grantTypeString == null)
133                        throw new ParseException("Missing \"grant_type\" parameter", OAuth2Error.INVALID_REQUEST);
134
135                if (! GrantType.parse(grantTypeString).equals(GRANT_TYPE))
136                        throw new ParseException("The \"grant_type\" must be " + GRANT_TYPE, OAuth2Error.UNSUPPORTED_GRANT_TYPE);
137
138                // Parse JWT assertion
139                String assertionString = params.get("assertion");
140
141                if (assertionString == null || assertionString.trim().isEmpty())
142                        throw new ParseException("Missing or empty \"assertion\" parameter", OAuth2Error.INVALID_REQUEST);
143
144                final JWT assertion;
145
146                try {
147                        assertion = JWTParser.parse(assertionString);
148                } catch (java.text.ParseException e) {
149                        throw new ParseException("The \"assertion\" is not a JWT: " + e.getMessage(), OAuth2Error.INVALID_REQUEST, e);
150                }
151
152                if (assertion instanceof SignedJWT) {
153                        return new JWTBearerGrant((SignedJWT)assertion);
154                } else if (assertion instanceof EncryptedJWT) {
155                        return new JWTBearerGrant((EncryptedJWT)assertion);
156                } else {
157                        throw new ParseException("The JWT assertion must not be unsecured (plain)", OAuth2Error.INVALID_REQUEST);
158                }
159        }
160}