Server-Side Sign in with Apple

Nov 15 2022 · Swift 5.6, macOS 12, iOS 15, Xcode 13.3

Part 2: Add Sign in with Apple to a Website

09. Authenticate Sign in with Apple Users on the Web

Episode complete

About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 08. Handle the Sign in with Apple Callback

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 09. Authenticate Sign in with Apple Users on the Web

The last piece of the puzzle is implementing the logic for completing the Sign in with Apple flow.

In the Vapor app, open WebiteController.swift and add a new request handler, appleAuthRedirectHandler(_:) that returns a Response:

func appleAuthRedirectHandler(_ req: Request) async throws -> Response {

}

First, decode the request body to SIWARedirectData - this matches the data sent in the form in the previous video:

let data = try req.content.decode(SIWARedirectData.self)

Next, get the application identifier from the environment variables that you set up when you configured the website in the Apple Developer Portal. Note that this is different to the app identifier used for the iOS app:

guard let appIdentifier = Environment.get("WEBSITE_APPLICATION_IDENTIFIER") else {
    throw Abort(.internalServerError)
}

Then, verify the token provided to ensure it’s a valid JWT:

let siwaToken = try await req.jwt.apple.verify(data.token, applicationIdentifier: appIdentifier)

Once you’ve passed this point you have a JWT you know came from Apple with a valid and authenticated user.

Next, see if an user exists with the Sign in with Apple identifier:

let user: User
if let userFound = try await User.query(on: req.db).filter(\.$siwaIdentifier == siwaToken.subject.value).first() {
  user = userFound
} else {

}

If the user doesn’t exist, search for them with their email to see if they are an existing user or not:

guard let email = data.email, let firstName = data.firstName, let lastName = data.lastName else {
  throw Abort(.badRequest)
}
if let existingUser = try await User.query(on: req.db).filter(\.$username == email).first() {

} else {

}

If the user already exists, update their Sign in with Apple identifier and save the user:

user = existingUser
user.siwaIdentifier = siwaToken.subject.value
try await user.save(on: req.db)

If the user is a new user, create the user and save them in the database:

let newUser = User(name: "\(firstName) \(lastName)", username: email, password: UUID().uuidString, siwaIdentifier: siwaToken.subject.value)
try await newUser.save(on: req.db)
user = newUser

Finally, log them in and redirect to the homepage:

req.auth.login(user)
return req.redirect(to: "/")

Register the route as a POST request to /login/siwa/handle in boot(_:):

authSessionsRoutes.post("login", "siwa", "handle", use: appleAuthRedirectHandler)

Build and run the app and then go to the URL from Ngrok in your browser. Make sure it matches the URL set in the developer portal and the environment variables.

Click Create an Acronym and you’ll be taken to the log in page. Click Sign in with Apple and complete the flow to log in. Click again on Create an Acronym and you’ll be taken to the create page now that you’re logged in. Congratulations! You’ve signed in with Apple on a website!