Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Next, you have to configure your web page for Sign in with Apple. Follow the gui
# Example
## Using `AppleAuthProvider.java` `AppleClientPrivateKeyFactory.java`
First order of business should be creating an instance of `ECPrivateKey` representing the client's(your) private key.<br/>
`AppleKeyProvider` can help you create a `ECPrivateKey` if you have your private key as string or stream (from a p8 for example).
`AppleClientPrivateKeyFactory` can help you create a `ECPrivateKey` if you have your private key as string or stream (from a p8 for example).

Creating a new instance of `AppleAuthProvider`, should be trivial at this point. The only two parameters that are not
self explanatory are the `SecretGenerator` and the collection of scopes.<br/>
Expand All @@ -30,10 +30,10 @@ Once you have your `AppleAuthProvider` instance you can:
## Handling initial response from Apple
After the user clicks on the "Sign in with Apple" button on your page they will be redirected to https://appleid.apple.com/.
After they provide their credentials Apple will make a POST request to the url that you have specified as Redirect URL.
It will contain a ```code``` field. Its contents is what should be handed down to `makeNewAuthorisationTokenRequest` in order retrieve thee authorization token (it will also contain the state used to create the redirect url).
It will contain a `code` field. Its contents is what should be handed down to `makeNewAuthorisationTokenRequest` in order retrieve the authorization token (it will also contain the state used to create the redirect url).
Keep in mind that tokens returned from Apple are short-lived, so you should create a session or a user in your system
using the returned ```AppleAuthorizationToken``` object. After that you can verify if the user is
still logged in using "Sign in with Apple" by retrieving a refresh token using the ```makeNewRefreshTokenRequest``` method.
using the returned `AppleAuthorizationToken` object. After that you can verify if the user is
still logged in using "Sign in with Apple" by retrieving a refresh token using the `makeNewRefreshTokenRequest` method.

```java
public class AppleIdTokenManager {
Expand All @@ -44,14 +44,14 @@ still logged in using "Sign in with Apple" by retrieving a refresh token using t
private static final String REDIRECT_URL = "Your redirect url";

public static void main(String[] args) throws IOException, InvalidKeySpecException {
//Generating your private key.
//This could be just a string containing the key.
// Generating your private key.
// This could be just a string containing the key.
InputStream pkStream = AppleIdTokenManager.class
.getClassLoader().getResourceAsStream("your_pk_file.p8");
AppleClientPrivateKeyFactory appleClientPrivateKeyFactory = new AppleClientPrivateKeyFactory();
ECPrivateKey privateKey = appleClientPrivateKeyFactory.getEcPrivateKey(pkStream);

//Creating provider instance.
// Creating provider instance.
SecretGenerator secretGenerator = new SecretGenerator();
AppleAuthProvider appleAuthProvider = new AppleAuthProvider(
CLIENT_ID,
Expand All @@ -63,20 +63,20 @@ still logged in using "Sign in with Apple" by retrieving a refresh token using t
REDIRECT_URL
);

//We are ready to start using the provider.
// We are ready to start using the provider.

//Generate a url and navigate the user to it.
// Generate a url and navigate the user to it.
String loginLink = appleAuthProvider.getLoginLink("Some form of state");

//Once the user is redirected back to our domain get the "code" in the request.
// Once the user is redirected back to our domain get the "code" in the request.
String authCode = "the code in the callback request";
//Now we can authenticate the user.
// Now we can authenticate the user.
AppleAuthorizationToken initialToken = appleAuthProvider.makeNewAuthorisationTokenRequest(authCode);
//After the authentication we should check (not more than once every once 24 hours) if the user still
// logged in using "Sign in with Apple" by retrieving a refresh token.
// After the authentication we should check (not more than once every 24h) if the user
// is still logged in using "Sign in with Apple" by retrieving a refresh token.
AppleAuthorizationToken refreshToken = appleAuthProvider.makeNewRefreshTokenRequest(initialToken
.getRefreshToken());

}
}
```
```
9 changes: 4 additions & 5 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,20 @@
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
<version>4.0.0</version>
</dependency>

<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.11.0</version>
<version>0.21.1</version>
</dependency>

<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.30.9</version>
<version>2.0.0</version>
</dependency>
</dependencies>


</project>
</project>
27 changes: 14 additions & 13 deletions src/main/java/com/accedia/apple/auth/AppleAuthProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
Expand All @@ -26,7 +26,8 @@
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

public class AppleAuthProvider {

Expand All @@ -45,7 +46,7 @@ public class AppleAuthProvider {
private final long secretLifeInSec;
private final GenericUrl appleAuthTokenUrl;
private final String appleAuthAuthorizationUrl;
private UserDataDeserializer userDataDeserializer;
private final UserDataDeserializer userDataDeserializer;
private final List<String> appleUserScopes;
private final String redirectUrl;
private final HttpTransport httpTransport;
Expand Down Expand Up @@ -81,11 +82,11 @@ public AppleAuthProvider(String clientId, String keyId, String teamId, SecretGen
DEFAULT_APPLE_AUTH_AUTHORIZE_URL,
new UserDataDeserializer(),
new NetHttpTransport(),
new JacksonFactory(),
new GsonFactory(),
DEFAULT_MAX_TIMEOUT_IN_SEC,
secretGenerator,
privateKey,
() -> Instant.now(),
Instant::now,
DEFAULT_SECRET_LIFE_IN_SEC,
new AppleKeyProvider(),
scopes,
Expand Down Expand Up @@ -146,7 +147,7 @@ public AppleAuthProvider(String clientId,
this.secretLifeInSec = secretLifeInSec;
this.userDataDeserializer = userDataDeserializer;
this.appleUserScopes = scopes != null ? ImmutableList.copyOf(
scopes.stream().map(AppleUserScope::getLiteral).collect(Collectors.toList())
scopes.stream().map(AppleUserScope::getLiteral).collect(toList())
) : null;
this.appleAuthTokenUrl = new GenericUrl(appleAuthTokenUrl);
this.jsonFactory = jsonFactory;
Expand All @@ -159,7 +160,7 @@ public AppleAuthProvider(String clientId,
this.httpTransport = httpTransport;

Algorithm validationAlg = Algorithm.RSA256(appleKeyProvider);
this.jwtVerifier = JWT.require(validationAlg)
this.jwtVerifier = JWT.require(validationAlg)
.build();

}
Expand All @@ -182,13 +183,13 @@ public String getLoginLink(String state) {
.setResponseTypes(Arrays.asList("code", "id_token"))
.setScopes(appleUserScopes)
.setState(state)
.set("response_mode", "form_post")//Could be parameterized based on scope.
.set("response_mode", "form_post") //Could be parameterized based on scope.
.build();
}

/**
* Makes an authorisation request. Retrieves an User's date from Apple. Use this object to create users or sessions.
* @param authCode Received from Apple after successfully redirecting the use.
* Makes an authorisation request. Retrieves a User's date from Apple. Use this object to create users or sessions.
* @param authCode Received from Apple after successfully redirecting the user.
* @throws IOException
*/
public AppleAuthorizationToken makeNewAuthorisationTokenRequest(String authCode) throws IOException {
Expand All @@ -201,7 +202,7 @@ public AppleAuthorizationToken makeNewAuthorisationTokenRequest(String authCode)
/**
* Verifies if a token is valid.
* Use this method to check daily if the user is still signed in on your app using Apple ID.
* @param refreshToken
* @param refreshToken
* @return
* @throws IOException
*/
Expand All @@ -218,13 +219,13 @@ private AppleAuthorizationToken executeTokenRequest(TokenRequest tokenRequest) t
TokenResponse tokenResponse = tokenRequest.execute();
Optional<String> idToken = Optional.ofNullable(tokenResponse.get("id_token")).map(Object::toString);
idToken.ifPresent(this::validateToken);
Optional<UserData> userData = idToken.map(userDataDeserializer::getUserDataFromIdToken);
UserData userData = idToken.map(userDataDeserializer::getUserDataFromIdToken).orElse(null);
return new AppleAuthorizationToken(
tokenResponse.getAccessToken(),
tokenResponse.getExpiresInSeconds(),
idToken.orElse(null),
tokenResponse.getRefreshToken(),
userData.orElse(null));
userData);
}

private void validateToken(String token) {
Expand Down