All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.authkit.servlet.DefaultAuthenticator Maven / Gradle / Ivy

There is a newer version: 0.3.0
Show newest version
package com.authkit.servlet;

import com.authkit.AuthKitException;
import com.authkit.AuthKitPrincipal;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import io.jsonwebtoken.*;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.*;

import static com.authkit.Util.required;

public class DefaultAuthenticator implements Authenticator {

    private static final Gson GSON = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy
            .LOWER_CASE_WITH_UNDERSCORES).create();

    private static final Set RESERVED_CLAIMS = new HashSet();

    static {
        RESERVED_CLAIMS.add("email");
        RESERVED_CLAIMS.add("family_name");
        RESERVED_CLAIMS.add("given_name");
        RESERVED_CLAIMS.add("roles");
        RESERVED_CLAIMS.add("permissions");
    }

    public static class OpenIdConfiguration {
        private String authorizationEndpoint;
        private String[] grantTypesSupported;
        private String[] idTokenSigningAlgValuesSupported;
        private String issuer;
        private String jwksUri;
        private String[] responseModesSupported;
        private String[] responseTypesSupported;
        private String revocationEndpoint;
        private String[] subjectTypesSupported;
        private String tokenEndpoint;
        private String userinfoEndpoint;
    }

    public static class Jwks {
        private Key[] keys;
    }

    public static class Key {
        private String alg;
        private String e;
        private String kid;
        private String kty;
        private String n;
        private String use;
        private String[] x5c;
        private String x5t;
    }

    // Visible for testing
    OpenIdConfiguration openIdConfiguration;
    Jwks jwks;
    PublicKey publicKey;
    JwtParser jwtParser;

    @Override
    public void init(Config config) {

        Map params = config.getInitParameters();

        String issuer = required(params.get("issuer"), "issuer");
        String audience = params.get("audience");

        loadOpenIdConfig(issuer);
        loadCertificates();

        JwtParserBuilder builder = Jwts.parserBuilder().setSigningKey(publicKey)
                .requireIssuer(openIdConfiguration.issuer);

        if (audience != null) {
            builder.requireAudience(audience);
        }

        jwtParser = builder.build();
    }

    private void loadOpenIdConfig(String issuer) {

        openIdConfiguration = loadObjectFromUrl(issuer + "/.well-known/openid-configuration",
                OpenIdConfiguration.class, null);
        jwks = loadObjectFromUrl(openIdConfiguration.jwksUri, Jwks.class, null);
    }

    private void loadCertificates() {

        try {

            String input = jwks.keys[0].x5c[0];
            byte[] inputBytes = Base64.getDecoder().decode(input);

            CertificateFactory fact = CertificateFactory.getInstance("X.509");
            ByteArrayInputStream is = new ByteArrayInputStream (inputBytes);
            X509Certificate cer = (X509Certificate) fact.generateCertificate(is);
            publicKey = cer.getPublicKey();

        } catch (Exception e) {
            throw new AuthKitException(e);
        }
    }

    @Override
    public AuthKitPrincipal authenticate(String token) {

        Jws jws = jwtParser.parseClaimsJws(token);

        Claims claims = jws.getBody();

        AuthKitPrincipal p = new AuthKitPrincipal();

        p.setSubject(claims.getSubject());
        p.setAudience(claims.getAudience());

        // TODO - This is called way too many times
        Map userinfo = loadObjectFromUrl(openIdConfiguration.userinfoEndpoint, Map.class, token);

        p.setEmail((String) userinfo.get("email"));
        p.setFamilyName((String) userinfo.get("family_name"));
        p.setGivenName((String) userinfo.get("given_name"));
        p.setIssuer(claims.getIssuer());
        p.setPermissions(stringArrayToStringSetClaim(claims, "permissions"));
        p.setRoles(stringArrayToStringSetClaim(claims, "roles"));
        p.setMetadata(new HashMap());

        for (Map.Entry e : userinfo.entrySet()) {
            if (! RESERVED_CLAIMS.contains(e.getKey())) {
                p.getMetadata().put(e.getKey(), e.getValue());
            }
        }

        return p;
    }

    private Set stringArrayToStringSetClaim(Claims claims, String name) {

        Set claim = new HashSet();

        ArrayList raw = claims.get(name, ArrayList.class);

        if (raw == null) {
            return null;
        }

        for (String c : raw) {
            claim.add(c);
        }

        return claim;
    }

    private  T loadObjectFromUrl(String url, Class objectType, String token)  {

        try {

            URL _url = new URL(url);
            HttpURLConnection con = (HttpURLConnection) _url.openConnection();
            con.setRequestMethod("GET");
            if (token != null) {
                con.setRequestProperty("Authorization", "Bearer " + token);
            }
            con.connect();

            int status = con.getResponseCode();

            switch (status) {
                case 200:
                case 201:
                    BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
                    StringBuilder sb = new StringBuilder();
                    String line;
                    while ((line = br.readLine()) != null) {
                        sb.append(line+"\n");
                    }

                    br.close();

                    return GSON.fromJson(sb.toString(), objectType);
                default:
                    throw new AuthKitException("Invalid status code: " + status);
            }

        } catch (Exception e) {
            throw new AuthKitException(e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy