Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Apple - new option for using private key instead secret key. #1019

Open
wants to merge 11 commits into
base: master
Choose a base branch
from

Conversation

hamrak
Copy link
Contributor

@hamrak hamrak commented May 4, 2023

Rather than static client secrets, Apple requires that you derive a client secret yourself from your private key. They use the JWT standard for this, using an elliptic curve algorithm with a P-256 curve and SHA256 hash. In other words, they use the ES256 JWT algorithm.

This PR add new configuration options that allow you to use private key instead of manually generating secret key every 6 months.

'key_id' => env('APPLE_KEY_ID'),
'team_id' => env('APPLE_TEAM_ID'),
'private_key' => env('APPLE_PRIVATE_KEY'), // Must be absolute path, e.g. /var/www/cert/AuthKey_XYZ.p8
'passphrase' => env('APPLE_PASSPHRASE'), // Set if your key have a passphrase.
'signer' => env('APPLE_SIGNER'), // Signer used for Configuration::forSymmetricSigner().

Need to merge #1018 first.

@atymic
Copy link
Member

atymic commented May 13, 2023

Could we get a docs like and a rebase please :)

@hamrak
Copy link
Contributor Author

hamrak commented May 13, 2023

I don't know what you mean exactly.
Official Apple documentation for creating secret token is here: https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens#3262048

I also put some description in README.md.

Can you tell me exactly what I should add? :)

return (new self(
new Request(),
config('services.apple.client_id'),
config('services.apple.client_secret'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this method still work if the secret is dervied?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it will work. I had to do that (create new object) because this function is called statically (don't know why) and it was necessary to call other non static functions.

@stale
Copy link

stale bot commented Aug 12, 2023

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Aug 12, 2023
@stale stale bot closed this Sep 17, 2023
@atymic atymic reopened this Sep 18, 2023
@pricop
Copy link

pricop commented Sep 22, 2023

This seems to be inspired from https://bannister.me/blog/generating-a-client-secret-for-sign-in-with-apple-on-each-request

Perhaps add support for private keys that are stored in the database / string format too?

My proposal would be something like:

$this->privateKey = $private_key_path; // Support for plain text private keys

if (!empty($private_key_path) && file_exists($private_key_path)) {
    $this->privateKey = file_get_contents($private_key_path);
}

This will ensure backwards compatibility for those of us who are already using this integration.

Thank you.

@stale stale bot removed the stale label Sep 22, 2023
@hamrak
Copy link
Contributor Author

hamrak commented Sep 24, 2023

pricop

Done ;)

Copy link

stale bot commented Dec 15, 2023

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Dec 15, 2023
@atymic
Copy link
Member

atymic commented Feb 7, 2024

Is this ready for review/merge?

@stale stale bot removed the stale label Feb 7, 2024
@pricop
Copy link

pricop commented Feb 7, 2024

Yes, it should be ready for review/merge. I've been using this successfully into production on multiple websites. This would be an awesome addition.

@hamrak
Copy link
Contributor Author

hamrak commented Feb 7, 2024

Yes, i use it in production too.

Copy link
Member

@atymic atymic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small change and a comment.

protected function getJwtConfig()
{
if (!$this->jwtConfig) {
$private_key_path = config('services.apple.private_key', '');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we access this via the standard method, ie add to

public static function additionalConfigKeys()
    {
        return ['private_key', 'otherkey...'];
    }
$this->getConfig('private_key', '')

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can. Changed.

$this->privateKey = file_get_contents($private_key_path);
}

$this->jwtConfig = Configuration::forSymmetricSigner(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm, there is no bc break here, existing integrations using the secret key will continiue to work fine?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, if you pass the path to the private key file, it will get the contents of the private key file, otherwise you put the private key contents by default.

@atymic
Copy link
Member

atymic commented Feb 10, 2024

@hamrak just a quick thing re; config and a question, then I will merge and tag new vers :)

@hamrak hamrak requested a review from atymic February 11, 2024 11:45
if (!empty($this->privateKey)) {
$appleToken = new AppleToken($this->getJwtConfig());
$this->clientSecret = $appleToken->generate();
config()->set('services.apple.client_secret', $this->clientSecret);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might need to set this to a variable on the class with above switch to $this->getConfig

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you think so?

{
return (new self(
new Request(),
config('services.apple.client_id'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
config('services.apple.client_id'),
$this->getConfig('client_id', '')

And other calls to config()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can call $this-> in static function, or ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh my bad, didn't realise it was static. Is that why it overrides the global config above?

{
return (new self(
new Request(),
config('services.apple.client_id'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh my bad, didn't realise it was static. Is that why it overrides the global config above?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants