Understanding Authentication in Vapor: Part II

Understanding Authentication in Vapor: Part II

In the first part of this series, we explored how to create a user model, implement basic authentication, and establish secure endpoints using Vapor’s ModelAuthenticatable. Now, let’s dive into the next steps: generating tokens for more secure user authentication, protecting routes, and using Vapor’s ModelTokenAuthenticatable.

Why Use Token-Based Authentication?

While Basic Authentication is straightforward, it’s not ideal for protecting all endpoints because it requires sending sensitive credentials (username and password) with every request. Token-based authentication resolves this by issuing a token during login that can be used for subsequent requests, improving both security and efficiency.

Setting Up the User Token Model

To enable token-based authentication, we first need a model to represent user tokens. Here’s an example:

final class UserToken: Model, Content {
    static let schema = "user_tokens"
    @ID(key: .id)
    var id: UUID?
    @Field(key: "value")
    var value: String
    @Parent(key: "user_id")
    var user: User

    init() { }

    init(id: UUID? = nil, value: String, userID: User.IDValue) {
        self.id = id
        self.value = value
        self.$user.id = userID
    }
}        

Each token is tied to a specific user and contains a value field, which holds the unique token string.

Migrating the User Token Model

To store user tokens in the database, create a migration:

extension UserToken {
    struct Migration: AsyncMigration {
        var name: String { "CreateUserToken" }
        func prepare(on database: Database) async throws {
            try await database.schema("user_tokens")
                .id()
                .field("value", .string, .required)
                .field("user_id", .uuid, .required, .references("users", "id"))
                .unique(on: "value")
                .create()
        }
        func revert(on database: Database) async throws {
            try await database.schema("user_tokens").delete()
        }
    }
}        

Remember to add this migration to app.migrations:

app.migrations.add(UserToken.Migration())        

Generating a Token

Now, let’s extend the User model to include a method for generating tokens:

extension User {
    func generateToken() throws -> UserToken {
        try .init(
            value: [UInt8].random(count: 16).base64,
            userID: self.requireID()
        )
    }
}        

This generates a secure, random token that can be returned to the client upon login.

Implementing Login with Tokens

Update the login route to create and return a token upon successful authentication:

let passwordProtected = app.grouped(User.authenticator())
passwordProtected.post("login") { req async throws -> UserToken in
    let user = try req.auth.require(User.self)
    let token = try user.generateToken()
    try await token.save(on: req.db)
    return token
}        

When a user logs in, they receive a token that can be used for further requests.

Protecting Routes with Tokens

To protect endpoints using the generated token, make the UserToken model conform to ModelTokenAuthenticatable:

extension UserToken: ModelTokenAuthenticatable {
    static let valueKey = \UserToken.$value
    static let userKey = \UserToken.$user
    var isValid: Bool {
        true
    }
}        

Now, you can use the UserToken authenticator to protect sensitive routes. For example:

let tokenProtected = app.grouped(UserToken.authenticator())
tokenProtected.get("me") { req -> User in
    try req.auth.require(User.self)
}        

This ensures that only authenticated users with a valid token can access the GET /me endpoint.

Testing

1. Login:

Send a POST request to /login with the user’s credentials. You’ll receive a token in response.

2. Access Protected Route:

Include the token in the Authorization header as a Bearer token to access protected routes:

Authorization: Bearer <token>        

Conclusion

By introducing token-based authentication, you’ve taken a significant step towards building a secure and scalable API. This approach minimizes the need to repeatedly send sensitive credentials and provides a more efficient way to manage user sessions.

Feel free to leave your questions and thoughts in the comments, or share your experiences with authentication in Vapor!

#Swift

#iOS

#Vapor

#Backend

? Dmitry Knauer

iOS Developer | Avito | Experience 5+ years

1 个月

Wow, this is so well explained! Now it’s clear why tokens are a must-have for APIs. Thanks for the simple and clear breakdown!

回复

Insightful!

Natan Mohart

Tech Entrepreneur | Team Lead & Software Engineer | Author & Speaker | Follow for daily posts about Mindset, Personal Growth, and Leadership

1 个月

Great continuation of the series! ?? Generating tokens for user authentication is crucial for enhancing security. I'm looking forward to seeing how you implement route protection and utilize Vapor's ModelTokenAuthenticatable. These steps are vital for building robust applications. Keep up the great work!

Davit Gasparyan

Frontend Developer @TechWings | React, TypeScript, JavaScript | Improving UX Through Scalable Solutions

1 个月

Great series so far! Diving into token-based authentication with Vapor sounds exciting, secure endpoints and protected routes are crucial for robust apps. Looking forward to seeing how it all comes together!

要查看或添加评论,请登录

Yuriy Gudimov的更多文章

  • Getting Started with JWT in Vapor

    Getting Started with JWT in Vapor

    When building web applications, secure and efficient authentication is critical. One of the most popular methods for…

    5 条评论
  • Understanding Authentication in Vapor: Part I

    Understanding Authentication in Vapor: Part I

    Authentication is a fundamental aspect of web applications, ensuring that users can verify their identity before…

    5 条评论
  • Leveraging APNs in Vapor for Seamless Push Notifications

    Leveraging APNs in Vapor for Seamless Push Notifications

    As my exploration of the Vapor framework continues, I’m excited to share my experience with the Apple Push Notification…

    6 条评论
  • Understanding Sessions in Vapor

    Understanding Sessions in Vapor

    In the world of web development, managing user sessions is crucial for providing a seamless experience. In this…

    5 条评论
  • Introducing Vapor Queues

    Introducing Vapor Queues

    In the world of web development, maintaining a responsive user experience is crucial. As applications grow in…

    3 条评论
  • Cracking LinkedIn SSI: Results & Tips

    Cracking LinkedIn SSI: Results & Tips

    Introduction Today marks the end of my second month working on improving my LinkedIn SSI. If you’re curious about my…

    29 条评论
  • Introduction to Testing in Vapor

    Introduction to Testing in Vapor

    As your readers dive into the world of Vapor, understanding the testing capabilities of this powerful Swift web…

    4 条评论
  • Understanding Middleware in Vapor

    Understanding Middleware in Vapor

    As we continue our journey into the Vapor framework, today we’ll explore an essential concept that underpins many web…

    6 条评论
  • Unlocking the Power of Redis in Vapor: A Step Towards Scalable Applications

    Unlocking the Power of Redis in Vapor: A Step Towards Scalable Applications

    As developers, we’re always looking for efficient ways to manage our applications' data, especially when it comes to…

    9 条评论
  • Leaf in Vapor: Building Dynamic HTML with Swift

    Leaf in Vapor: Building Dynamic HTML with Swift

    In today's digital landscape, creating dynamic web applications is essential. For developers using Vapor, an efficient…

    8 条评论

社区洞察

其他会员也浏览了