001package com.nimbusds.oauth2.sdk;
002
003
004import java.util.LinkedHashMap;
005import java.util.Map;
006
007import net.jcip.annotations.Immutable;
008
009import com.nimbusds.jwt.JWT;
010import com.nimbusds.jwt.JWTParser;
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>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and
020 *         Authorization Grants (draft-ietf-oauth-jwt-bearer-10), section-2.1.
021 *     <li>Assertion Framework for OAuth 2.0 Client Authentication and
022 *         Authorization Grants (draft-ietf-oauth-assertions-16), section 4.1.
023 * </ul>
024 */
025@Immutable
026public final 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 JSON Web Token (JWT) bearer assertion grant.
043         *
044         * @param assertion The JSON Web Token (JWT) assertion. Must not be
045         *                  {@code null}.
046         */
047        public JWTBearerGrant(final JWT assertion) {
048
049                super(GRANT_TYPE);
050
051                if (assertion == null)
052                        throw new IllegalArgumentException("The JWT assertion must not be null");
053
054                this.assertion = assertion;
055        }
056
057
058        /**
059         * Gets the JSON Web Token (JWT) bearer assertion.
060         *
061         * @return The JWT bearer assertion.
062         */
063        public JWT getJWTAssertion() {
064
065                return assertion;
066        }
067
068
069        @Override
070        public String getAssertion() {
071
072                return assertion.serialize();
073        }
074
075
076        @Override
077        public Map<String,String> toParameters() {
078
079                Map<String,String> params = new LinkedHashMap<>();
080                params.put("grant_type", GRANT_TYPE.getValue());
081                params.put("assertion", assertion.serialize());
082                return params;
083        }
084
085
086        /**
087         * Parses a JWT bearer grant from the specified parameters.
088         *
089         * <p>Example:
090         *
091         * <pre>
092         * grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer
093         * &assertion=eyJhbGciOiJFUzI1NiJ9.eyJpc3Mi[...omitted for brevity...].
094         * J9l-ZhwP[...omitted for brevity...]
095         * </pre>
096         *
097         * @param params The parameters.
098         *
099         * @return The JWT bearer grant.
100         *
101         * @throws ParseException If parsing failed.
102         */
103        public static JWTBearerGrant parse(final Map<String,String> params)
104                throws ParseException {
105
106                // Parse grant type
107                String grantTypeString = params.get("grant_type");
108
109                if (grantTypeString == null)
110                        throw new ParseException("Missing \"grant_type\" parameter", OAuth2Error.INVALID_REQUEST);
111
112                if (! GrantType.parse(grantTypeString).equals(GRANT_TYPE))
113                        throw new ParseException("The \"grant_type\" must be " + GRANT_TYPE, OAuth2Error.UNSUPPORTED_GRANT_TYPE);
114
115                // Parse JWT assertion
116                String assertionString = params.get("assertion");
117
118                if (assertionString == null || assertionString.trim().isEmpty())
119                        throw new ParseException("Missing or empty \"assertion\" parameter", OAuth2Error.INVALID_REQUEST);
120
121                JWT assertion;
122
123                try {
124                        assertion = JWTParser.parse(assertionString);
125                } catch (java.text.ParseException e) {
126                        throw new ParseException("The \"assertion\" is not a JWT: " + e.getMessage(), OAuth2Error.INVALID_REQUEST, e);
127                }
128
129                return new JWTBearerGrant(assertion);
130        }
131}