Chapters

Hide chapters

Server-Side Swift with Vapor

Third Edition - Early Acess 1 · iOS 13 · Swift 5.2 - Vapor 4 Framework · Xcode 11.4

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section I: Creating a Simple Web API

Section 1: 13 chapters
Show chapters Hide chapters

23. GitHub Authentication
Written by Tim Condon

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Note: This update is an early-access release. This chapter has not yet been updated to Vapor 4.

In the previous chapter, you learned how to authenticate users using Google. In this chapter, you’ll see how to build upon this and allow users to log in with their GitHub accounts.

Setting up your application with GitHub

To be able to use GitHub OAuth in your application, you must first register the application with GitHub. In your browser, go to https://github.com/settings/developers. Click Register a new application:

Note: You must have a GitHub account to complete this chapter. If you don’t have one, visit https://github.com/join to create one. This chapter also assumes you added Imperial as a dependency to your project in the previous chapter.

Fill in the form with an appropriate name, e.g. Vapor TIL. Set the Homepage URL to http://localhost:8080 for this application and provide a sensible description. Set the Authorization callback URL to http://localhost:8080/oauth/github. This is the URL that GitHub redirects back to once users have allowed your application access to their data:

Click Register application. After it creates the application, the site takes you back to the application’s information page. That page provides the client ID and client secret that you need:

Note: You must keep these safe and secure. Your secret allows you access to GitHub’s APIs and you should not share or check the secret into source control. You should treat it like a password.

Integrating with Imperial

Now that you’ve registered your application with GitHub, you can start integrating Imperial. Open ImperialController.swift and add the following under processGoogleLogin(request:token:):

func processGitHubLogin(request: Request, token: String)
  throws -> Future<ResponseEncodable> {
    return request.future(request.redirect(to: "/"))
}
guard let githubCallbackURL =
  Environment.get("GITHUB_CALLBACK_URL") else {
    fatalError("GitHub callback URL not set")
}
try router.oAuth(
  from: GitHub.self,
  authenticate: "login-github",
  callback: githubCallbackURL,
  completion: processGitHubLogin)

Integrating with web authentication

As in the previous chapter, it’s important to match the experience for a regular login. Again, you’ll create a new user when a user logs in with GitHub for the first time. You can use GitHub’s API with the user’s OAuth token.

struct GitHubUserInfo: Content {
  let name: String
  let login: String
}
extension GitHub {
  // 1
  static func getUser(on request: Request)
  	throws -> Future<GitHubUserInfo> {
  	  // 2
      var headers = HTTPHeaders()
      headers.bearerAuthorization =
        try BearerAuthorization(token: request.accessToken())

      // 3
      let githubUserAPIURL = "https://api.github.com/user"
      // 4
      return try request
        .client()
        .get(githubUserAPIURL, headers: headers)
        .map(to: GitHubUserInfo.self) { response in
          // 5
          guard response.http.status == .ok else {
            // 6
            if response.http.status == .unauthorized {
              throw Abort.redirect(to: "/login-github")
            } else {
              throw Abort(.internalServerError)
            }
          }
          // 7
          return try response.content
            .syncDecode(GitHubUserInfo.self)
      }
  }
}
// 1
return try GitHub
  .getUser(on: request)
  .flatMap(to: ResponseEncodable.self) { userInfo in
    // 2
    return User
      .query(on: request)
      .filter(\.username == userInfo.login)
      .first()
      .flatMap(to: ResponseEncodable.self) { foundUser in
        guard let existingUser = foundUser else {
          // 3
          let user = User(name: userInfo.name,
                          username: userInfo.login,
                          password: UUID().uuidString)
          // 4
          return user
            .save(on: request)
            .map(to: ResponseEncodable.self) { user in
              // 5
              try request.authenticateSession(user)
              return request.redirect(to: "/")
          }
        }
        // 6
        try request.authenticateSession(existingUser)
        return request.future(request.redirect(to: "/"))
    }
}
<a href="/login-github">
  <img class="mt-3" src="/images/sign-in-with-github.png"
   alt="Sign In With GitHub">
</a>

Where to go from here?

In this chapter, you learned how to integrate GitHub login into your website using Imperial and OAuth. This complements the Google and first-party sign in experiences. This allows your users to choose a range of options for authentication.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now