Git Product home page Git Product logo

laravel-sign-in-with-apple's Introduction

Sign In With Apple for Laravel

repository-open-graph-template

Supporting This Package

This is an MIT-licensed open source project with its ongoing development made possible by the support of the community. If you'd like to support this, and our other packages, please consider sponsoring us via the button above.

We thank the following sponsors for their generosity, please take a moment to check them out:

Table of Contents

Requirements

  • PHP 7.3+
  • Laravel 8.0+
  • Socialite 5.0+
  • Apple Developer Subscription

Installation

siwa-video-cover

  1. Install the composer package:

    composer require genealabs/laravel-sign-in-with-apple

    We also recommend using geneaLabs/laravel-socialiter to automatically manage user resolution and persistence:

    composer require genealabs/laravel-socialiter

Configuration

  1. Create an App ID for your website (https://developer.apple.com/account/resources/identifiers/list/bundleId) with the following details:

    • Platform: iOS, tvOS, watchOS (I'm unsure if either choice has an effect for web apps)
    • Description: (something like "example.com app id")
    • Bundle ID (Explicit): com.example.id (or something similar)
    • Check "Sign In With Apple"
  2. Create a Service ID for your website (https://developer.apple.com/account/resources/identifiers/list/serviceId) with the following details:

    • Description: (something like "example.com service id")
    • Identifier: com.example.service (or something similar)
    • Check "Sign In With Apple"
    • Configure "Sign In With Apple":
      • Primary App Id: (select the primary app id created in step 1)
      • Web Domain: example.com (the domain of your web site)
      • Return URLs: https://example.com/apple-signin (the route pointing to the callback method in your controller)
      • Click "Save".
      • Click the "Edit" button to edit the details of the "Sign In With Apple" configuration we just created.
      • If you haven't verified the domain yet, download the verification file, upload it to https://example.com/.well-known/apple-developer-domain-association.txt, and then click the "Verify" button.
  3. Create a Private Key for your website (https://developer.apple.com/account/resources/authkeys/list) with the following details:

    • Key Name:
    • Check "Sign In With Apple"
    • Configure "Sign In With Apple":
      • Primary App ID: (select the primary app id created in step 1)
      • Click "Save"
    • Click "Continue"
    • Click "Register"
    • Click "Download"
    • Rename the downloaded file to key.txt
  4. Create your app's client secret:

    • Install the JWT Gem:

      sudo gem install jwt
    • Create a file called client_secret.rb to process the private key:

      require 'jwt'
      
      key_file = 'key.txt'
      team_id = ''
      client_id = ''
      key_id = ''
      
      ecdsa_key = OpenSSL::PKey::EC.new IO.read key_file
      
      headers = {
      'kid' => key_id
      }
      
      claims = {
          'iss' => team_id,
          'iat' => Time.now.to_i,
          'exp' => Time.now.to_i + 86400*180,
          'aud' => 'https://appleid.apple.com',
          'sub' => client_id,
      }
      
      token = JWT.encode claims, ecdsa_key, 'ES256', headers
      
      puts token
    • Fill in the following fields:

      • team_id: This can be found on the top-right corner when logged into your Apple Developer account, right under your name.
      • client_id: This is the identifier from the Service Id created in step 2 above, for example com.example.service
      • key_id: This is the identifier of the private key created in step 3 above.
    • Save the file and run it from the terminal. It will spit out a JWT which is your client secret, which you will need to add to your .env file in the next step.

      ruby client_secret.rb
  5. Set the necessary environment variables in your .env file:

    SIGN_IN_WITH_APPLE_LOGIN="/apple/login/controller/login/action"
    SIGN_IN_WITH_APPLE_REDIRECT="/apple/login/controller/callback/action"
    SIGN_IN_WITH_APPLE_CLIENT_ID="your app's service id as registered with Apple"
    SIGN_IN_WITH_APPLE_CLIENT_SECRET="your app's client secret as calculated in step 4"

Implementation

Button

Add the following blade directive to your login page:

@signInWithApple($color, $hasBorder, $type, $borderRadius)
Parameter Definition
$color String, either "black" or "white.
$hasBorder Boolean, either true or false.
$type String, either "sign-in" or "continue".
$borderRadius Integer, greater or equal to 0.

Controller

This implementation uses Socialite to get the login credentials. The following is an example implementation of the controller:

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use GeneaLabs\LaravelSocialiter\Facades\Socialiter;
use Laravel\Socialite\Facades\Socialite;

class AppleSigninController extends Controller
{
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    public function login()
    {
        return Socialite::driver("sign-in-with-apple")
            ->scopes(["name", "email"])
            ->redirect();
    }

    public function callback(Request $request)
    {
        // get abstract user object, not persisted
        $user = Socialite::driver("sign-in-with-apple")
            ->user();
        
        // or use Socialiter to automatically manage user resolution and persistence
        $user = Socialiter::driver("sign-in-with-apple")
            ->login();
    }
}

Note that when processing the returned $user object, it is critical to know that the sub element is the unique identifier for the user, NOT the email address. For more details, visit https://developer.apple.com/documentation/signinwithapplerestapi/authenticating_users_with_sign_in_with_apple.


Credits

  1. https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple
  2. https://developer.apple.com/sign-in-with-apple/get-started

Commitment to Quality

During package development I try as best as possible to embrace good design and development practices, to help ensure that this package is as good as it can be. My checklist for package development includes:

  • ✅ Achieve as close to 100% code coverage as possible using unit tests.
  • ✅ Eliminate any issues identified by SensioLabs Insight and Scrutinizer.
  • ✅ Be fully PSR1, PSR2, and PSR4 compliant.
  • ✅ Include comprehensive documentation in README.md.
  • ✅ Provide an up-to-date CHANGELOG.md which adheres to the format outlined at http://keepachangelog.com.
  • ✅ Have no PHPMD or PHPCS warnings throughout all code.

Contributing

Please observe and respect all aspects of the included Code of Conduct.

Reporting Issues

When reporting issues, please fill out the included template as completely as possible. Incomplete issues may be ignored or closed if there is not enough information included to be actionable.

Submitting Pull Requests

Please review the Contribution Guidelines. Only PRs that meet all criterium will be accepted.

If you ❤️ open-source software, give the repos you use a ⭐️.

We have included the awesome symfony/thanks composer package as a dev dependency. Let your OS package maintainers know you appreciate them by starring the packages you use. Simply run composer thanks after installing this package. (And not to worry, since it's a dev-dependency it won't be installed in your live environment.)

laravel-sign-in-with-apple's People

Contributors

gnative avatar laravel-shift avatar mikebronner avatar mohamed-aiman avatar pgrimaud avatar poldixd avatar tahiaji avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laravel-sign-in-with-apple's Issues

Get firstName and lastName.

Is there an easy way to pull in the user's first name and last name separately? I want to treat these as different fields in my app. Thanks!

Class not found on case sensitive servers

Class 'GeneaLabs\LaravelSignInWithApple\Providers\SignInWithAppleProvider' not found
error thrown on case sensitive server.

Class file name is SigninWithAppleProvider.php when it should be SignInWithAppleProvider.php
Capital I on SignIn.

as referenced in the ServiceProvider.php

 return $socialite
                    ->buildProvider(SignInWithAppleProvider::class, $config);

How to manage multiple callbacks for each user role?

I am working on project where we have 2 different types of users, "doctors" and "patient", I have setup doctor sign in with apple, but how I can add another redirect url for "patient" too. so when Patient will login they will go to there controller as same doctor is doing
Note: I have added both callback in apple account, only issue is remaining from website side, when I call from patient login it still go to doctor controller after callback.

Update configuration.

  • Remove default routes from configuration.
  • Allow routes to be overridden in configuration.

User is not redirected after authenticated.

I'm having issues trying to redirect user after successful login. Or even, just redirect to homepage(or anypage) on the apple callback post routes controller.

// web.php

Route::get('/redirect/apple', 'Auth\LoginController@redirectToApple');
Route::post('callback/apple', 'Auth\LoginController@handleAppleCallback');
// .env
SIGN_IN_WITH_APPLE_LOGIN=https://app.com/login/apple
SIGN_IN_WITH_APPLE_REDIRECT=https://app.com/callback/apple
//LoginController.php


	public function redirectToApple()
	{
//sucessfully redirects/prompts Sign in with Apple URL
return Socialite::driver("sign-in-with-apple")
				->scopes(["name", "email"])
				->redirect();

	}

public function handleAppleCallback(){
$user = Socialite::driver("sign-in-with-apple")->user();
//this $user has correct data as expected;

//HERE are some auth() codes that successfully logs in the user to app. 

//!!! THIS DOESN"T REDIRECT. RATHER REDIRECTS TO EMPTY PAGE ON /callback/apple. 
return redirect(url('/'));

}

Tried other ways to test like :

public function handleAppleCallback(){
$user = Socialite::driver("sign-in-with-apple")->user();

//This doesn't redirect either, but again $user has all data needed.
return redirect(url('/'));

I also created a common handleCallbackForLogins method, that handles every services I use. There, they are all able to redirect successfully but when the service is of apple, it just doesn't redirect. What could be the cause here?

Thanks!

Your requirements could not be resolved to an installable set of packages

Problem 1
- Root composer.json requires genealabs/laravel-sign-in-with-apple ^0.5.0 -> satisfiable by genealabs/laravel-sign-in-with-apple[0.5.0].
- genealabs/laravel-sign-in-with-apple 0.5.0 requires illuminate/support ^8.0 -> found illuminate/support[v8.0.0, ..., v8.26.1] but these were not loaded, likely because it conflicts with another require.

redirect URL doesn't contain the authorization code

the sign in works fine but in the response when I retun the user variable instead of this :

image

it gives me this :

image

here is my code :

public function handleAppleCallback(Request $request)
{
    // get abstract user object, not persisted
    $user = Socialite::driver("sign-in-with-apple")
        ->stateless()
        ->user();

    $this->_registerOrLoginUser($user);
   $user->token;
    return response()->json($user);
}

Anyone knows how can I get the auth code ?

Using as a stateless provider

Hi
We are building an mobile app and would like to use this provider with an Laravel backend.
But have a question.

For Facebook and Google we use:
$socialiteUser = Socialite::driver('facebook')->stateless()->userFromToken($request->token);
Where $request->token is the token sent from the app.

Both the Facebook and the Google provider now runs: getUserByToken() and does an request to verify the user. See:
https://github.com/laravel/socialite/blob/4.0/src/Two/GoogleProvider.php#L61

But in this provider the getUserByToken() function just decodes the token:
https://github.com/GeneaLabs/laravel-sign-in-with-apple/blob/1a2aabe7146ecd0dc7876e49d6891b29203649f7/src/Providers/SignInWithAppleProvider.php#L83

Wouldn't it be a good idea to be consistent with how the other providers work? Or is there something specific with Apple login that works different? Or could this provider still work but call some other function to verify the user?

I understand that this provider is build for the web implementation first but just wanted to raise the question!

Otherwise, great work!

Error while getting user via access token

I successfully receive access token using firebase from my app but after sending it to my api server errors occur.

$providedUser = Socialite::driver($driver)->stateless()->userFromToken($accessToken);

This code is working for google, facebook, linkedin but throws error for apple.

[2020-08-18 11:55:32] production.ERROR: Argument 1 passed to GeneaLabs\LaravelSignInWithApple\Providers\SignInWithAppleProvider::mapUserToObject() must be of the type array, null given, called in /var/www/vhosts/******.com/vendor/laravel/socialite/src/Two/AbstractProvider.php on line 231 {"exception":"[object] (TypeError(code: 0): Argument 1 passed to GeneaLabs\\LaravelSignInWithApple\\Providers\\SignInWithAppleProvider::mapUserToObject() must be of the type array, null given, called in /var/www/vhosts/******.com/vendor/laravel/socialite/src/Two/AbstractProvider.php on line 231 at /var/www/vhosts/******.com/vendor/genealabs/laravel-sign-in-with-apple/src/Providers/SignInWithAppleProvider.php:100)
[stacktrace]
#0 /var/www/vhosts/******.com/vendor/laravel/socialite/src/Two/AbstractProvider.php(231): GeneaLabs\\LaravelSignInWithApple\\Providers\\SignInWithAppleProvider->mapUserToObject()
#1 /var/www/vhosts/******.com/app/Http/Controllers/Api/App/AuthController.php(134): Laravel\\Socialite\\Two\\AbstractProvider->userFromToken()

Edit: used idToken instead of access_token now it works.

How to configure for Sign-In only?

How can i configure this so that it only checks against the database for existing users and will log them in only if the emails match?

In other words, how would I use this and prevent signing up?

[Question] Do you need a paid Apple Developer account to use Sign in With Apple

I'm working on a web page proof of concept and want to use this package to use the Sign in with Apple, but I can't find anywhere to set up my api keys without paying for an Apple Developer Account. I'm not making any iOS or mac apps, just strictly want to build out a couple web pages.

Does anyone know if I can create the api keys without a paid apple dev account?

How to generate the client_secret by php laravel instead of RR script.

Hi team,

When generating the client secret at step 4 in below guideline, there was an expired time for the key. That's mean after this amount of time, the client secret will get expired and we have manually run the RR script again and update the client secret.
https://github.com/GeneaLabs/laravel-sign-in-with-apple#configuration

Is there a way so that we can generate the client secret using php/laravel code, so that we can control the expiry time and automatically run the code and get a new client secret ?

Thanks,
Son

getUserByToken($token) results in error

Calling Socialite::driver('sign-in-with-apple')->userFromToken($token); results in an error:

Argument 1 passed to GeneaLabs\LaravelSignInWithApple\Providers\SignInWithAppleProvider::mapUserToObject() must be of the type array, null given, called in /var/www/vendor/laravel/socialite/src/Two/AbstractProvider.php on line 232

error 500

Сайт appleid.apple.com пока не может обработать этот запрос.
HTTP ERROR 500

Response error from Apple invalid_client

Hi, after rediretion to callback method, got this error:
"Client error: POST https://appleid.apple.com/auth/token resulted in a 400 Bad Request response:\n{"error":"invalid_client"}\n"

Shorten config keys

All factory included socialite packages are using relatively short keys like GOOGLE_CLIENT_ID, I think it would make reading the config and env easier if this package would use APPLE_CLIENT_ID. The unnecessary (in my opinion) SIGN_IN_WITH_ part just adds visual clutters.

No email or name returned.

Hello,

Thanks for the great package.
I've done all the steps to work with Apple Sign-In but the result that returned have null name & email.

Laravel\Socialite\Two\User {#866 ▼
  +token: "ab37590f0496e4489b59b6c755f9b4c8d.0.msqsw.FQVbQXV-__jT3fqhkkatXQ"
  +refreshToken: "rf6bd2934e66147248597f58c1aa5e2e7.0.msqsw.Bu2vbPnEyLeib8bb1tNXrA"
  +expiresIn: 3600
  +id: "002026.9ebcbe224278487abc92c6f3f5c9b363.0247"
  +nickname: null
  +name: null
  +email: null
  +avatar: null
  +user: array:8 [▼
    "iss" => "https://appleid.apple.com"
    "aud" => "com.sdccards.service-id2"
    "exp" => 1585279148
    "iat" => 1585278548
    "sub" => "002026.9ebcbe224278487abc92c6f3f5c9b363.0247"
    "at_hash" => "yHuiSiPqGiPE-kkZ0Cc3jw"
    "auth_time" => 1585278546
    "nonce_supported" => true
  ]
}

I'm not sure if I missed something during the installation or the configuration of the package.
Any idea why this happens?
Thanks a lot

Login with an account but access was revoked

It's a bit strange and I've created a support topic at Apple already. But perhaps somebody from the community can help me.

After creating the app, service id and key and implementing all at the first time, I authenticated against Apple and they wanted to create a new account with my email. The authentication was successful and all went fine. Afterwards I've revoked the app from "Sign in with Apple" from my apple id to test again the sign up process. But then they don't ask my to create an account with that email address, they try to login with my apple id but then I get an error because the email address on the access token request is only popluated at the first request (when I create an account).

Anyone from the community had this behaviour before?

Thanks in advance!

can't install with laravel 8.0

getting this error when installing

composer require genealabs/laravel-sign-in-with-apple
Using version ^0.6.0 for genealabs/laravel-sign-in-with-apple
./composer.json has been updated
Running composer update genealabs/laravel-sign-in-with-apple
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - Root composer.json requires genealabs/laravel-sign-in-with-apple ^0.6.0 -> satisfiable by genealabs/laravel-sign-in-with-apple[0.6.0].
    - genealabs/laravel-sign-in-with-apple 0.6.0 requires illuminate/support ^9.0 -> found illuminate/support[v9.0.0-beta.1, ..., 9.x-dev] but these were not loaded, likely because it conflicts with another require.

You can also try re-running composer require with an explicit version constraint, e.g. "composer require genealabs/laravel-sign-in-with-apple:*" to figure out if any version is installable, or "composer require genealabs/laravel-sign-in-with-apple:^2.1" if you know which you need.

Installation failed, reverting ./composer.json and ./composer.lock to their original content.

Thanks for your time!

Apple is not redirect to url callback

although the callback url is set correctly, after my login, apple redirect to my index and not to my callback route. Has anyone had this problem?

Error name class

When you install the package the name of the file in the vendor is "SigninWithAppleProvider",
but it looks for it with the capital "i" "SignInWithAppleProvider", so when this plugin is uploaded on case sensitive servers this class is not found.

We have solved this error by modifying the name of the file as we explained above, although we understand that it should be fixed in a later version of this fantastic package.

Would this library work for a rest endpoint?

Forgive me if this is not acceptable. I don't have much experience with Laravel so this might be a dumb question.

I'm making the backend/services of my iOS app in Laravel, so I'm not using it as a website, and I was looking to implement the auth flow on the server and found your package. But on the Readme I only see how to use it with the SIWA button on the website.

Would this work without a website and only through a REST api?

Thank you!

CSRF Token Mismatch

Hi!

When the callback returns from the apple servers I get a 419 error from Laravel. Disabling the VerifyCsrfToken middleware fixes this but is obviously not a fix.

Laravel framework version: v8.13.0
laravel-sign-in-with-apple version: 0.5.0
socialite version: 5.1.0

My naive guess is that this is actually a laravel, apple or configuration issue as the request somehow returns with a POST instead of a GET with the state token in the request instead of the header.

Laravel 7 support ?

Laravel version: 7.28.4
PHP version: 7.4.10

composer require genealabs/laravel-sign-in-with-apple

Using version ^0.5.0 for genealabs/laravel-sign-in-with-apple
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

Problem 1
- Installation request for genealabs/laravel-sign-in-with-apple ^0.5.0 -> satisfiable by genealabs/laravel-sign-in-with-apple[0.5.0].
- Conclusion: remove laravel/framework v7.28.4
- Conclusion: don't install laravel/framework v7.28.4
- genealabs/laravel-sign-in-with-apple 0.5.0 requires illuminate/support ^8.0 -> satisfiable by laravel/framework[8.x-dev], illuminate/support[8.x-dev, v8.0.0, v8.0.1, v8.0.2, v8.0.3, v8.0.4, v8.1.0, v8.10.0, v8.2.0, v8.3.0, v8.4.0, v8.5.0, v8.6.0, v8.7.0, v8.7.1, v8.8.0, v8.9.0].
- Can only install one of: laravel/framework[8.x-dev, v7.28.4].
- don't install illuminate/support 8.x-dev|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.0.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.0.1|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.0.2|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.0.3|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.0.4|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.1.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.10.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.2.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.3.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.4.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.5.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.6.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.7.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.7.1|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.8.0|don't install laravel/framework v7.28.4
- don't install illuminate/support v8.9.0|don't install laravel/framework v7.28.4
- Installation request for laravel/framework (locked at v7.28.4, required as ^7.24) -> satisfiable by laravel/framework[v7.28.4].

Installation failed, reverting ./composer.json to its original content.

Why access_token instead of id_token?

Wonder, why not return id_token instead access_token? If id_token is return, then userFromToken() will not encounter mapUserToObject() error.

    public function user()
    {
        ...
        return $user
            ->setToken(Arr::get($response, 'access_token'))
            ->setRefreshToken(Arr::get($response, 'refresh_token'))
            ->setExpiresIn(Arr::get($response, 'expires_in'));
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.