Sign in with Apple Java User Verification

Çağdaş Tunca :

I've implemented the app side of the new apple feature "Sign in with Apple" but i'm unable to verificate with authorizationCode in my backend. My backend is written in java and i'm unable to generate JWT and communicate with Apple servers.

Çağdaş Tunca :

First go developer.apple.com -> Certificates, Identifiers & Profiles -> Keys. Generate a key for Apple Sign in and download this key. You can not download this key again so keep it in a safe place and don't share with others. Also your Key ID shown here note this, you'll need this later. You'll also need team id. If you don't know it, it's written top right of the page like YOURNAME - XX0XX00XXX.

You will basicly follow these steps.

1.Generate JWT from your key

2.Send auth code with your token

3.Decode response

public class AppleLoginUtil {
private static String APPLE_AUTH_URL = "https://appleid.apple.com/auth/token";

private static String KEY_ID = "**********";
private static String TEAM_ID = "**********";
private static String CLIENT_ID = "com.your.bundle.id";

private static PrivateKey pKey;

private static PrivateKey getPrivateKey() throws Exception {
//read your key
    String path = new ClassPathResource("apple/AuthKey.p8").getFile().getAbsolutePath();

    final PEMParser pemParser = new PEMParser(new FileReader(path));
    final JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
    final PrivateKeyInfo object = (PrivateKeyInfo) pemParser.readObject();
    final PrivateKey pKey = converter.getPrivateKey(object);

    return pKey;
}

private static String generateJWT() throws Exception {
    if (pKey == null) {
        pKey = getPrivateKey();
    }

    String token = Jwts.builder()
            .setHeaderParam(JwsHeader.KEY_ID, KEY_ID)
            .setIssuer(TEAM_ID)
            .setAudience("https://appleid.apple.com")
            .setSubject(CLIENT_ID)
            .setExpiration(new Date(System.currentTimeMillis() + (1000 * 60 * 5)))
            .setIssuedAt(new Date(System.currentTimeMillis()))
            .signWith(pKey, SignatureAlgorithm.ES256)
            .compact();

    return token;
}

/*
* Returns unique user id from apple
* */
public static String appleAuth(String authorizationCode) throws Exception {

    String token = generateJWT();

    HttpResponse<String> response = Unirest.post(APPLE_AUTH_URL)
            .header("Content-Type", "application/x-www-form-urlencoded")
            .field("client_id", CLIENT_ID)
            .field("client_secret", token)
            .field("grant_type", "authorization_code")
            .field("code", authorizationCode)
            .asString();

    TokenResponse tokenResponse=new Gson().fromJson(response.getBody(),TokenResponse.class);
    String idToken = tokenResponse.getId_token();
    String payload = idToken.split("\\.")[1];//0 is header we ignore it for now
    String decoded = new String(Decoders.BASE64.decode(payload));

    IdTokenPayload idTokenPayload = new Gson().fromJson(decoded,IdTokenPayload.class);

   return idTokenPayload.getSub();
}


}

I've used BouncyCastle jjwt for generating token. And also unirest and gson for rest calls.

 <!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on -->
    <dependency>
        <groupId>org.bouncycastle</groupId>
        <artifactId>bcpkix-jdk15on</artifactId>
        <version>1.63</version>
    </dependency>

<!--JJWT-->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-api</artifactId>
        <version>0.10.7</version>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-impl</artifactId>
        <version>0.10.7</version>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt-jackson</artifactId>
        <version>0.10.7</version>
        <scope>runtime</scope>
    </dependency>

<!--UNIREST-->
    <dependency>
        <groupId>com.mashape.unirest</groupId>
        <artifactId>unirest-java</artifactId>
        <version>1.4.9</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.3.6</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpasyncclient</artifactId>
        <version>4.0.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpmime</artifactId>
        <version>4.3.6</version>
    </dependency>
    <dependency>
        <groupId>org.json</groupId>
        <artifactId>json</artifactId>
        <version>20140107</version>
    </dependency>

I've also parsed the responses to these classes if you wanted to know.

public class TokenResponse {

private String access_token;
private String token_type;
private Long expires_in;
private String refresh_token;
private String id_token;

..getters and setters}

public class IdTokenPayload {

private String iss;
private String aud;
private Long exp;
private Long iat;
private String sub;//users unique id
private String at_hash;
private Long auth_time;

..getters and setters}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=304699&siteId=1