Skip to content

Commit

Permalink
Merge pull request #1 from skelpo/develop
Browse files Browse the repository at this point in the history
Docs Update. Some Other Minor Fixes Not Worth Mentioning
  • Loading branch information
calebkleveter authored Apr 20, 2018
2 parents 0410ae7 + 1b23004 commit dd47e20
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 31 deletions.
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import PackageDescription
let package = Package(
name: "JWTMiddleware",
products: [
.library(name: "JWTMiddleware", targets: ["JWTMiddleware"]),
.library(name: "JWTMiddleware", targets: ["JWTMiddleware", "JWTAuthenticatable"]),
],
dependencies: [
.package(url: "https://github.com/skelpo/vapor-request-storage.git", from: "0.1.0"),
Expand Down
96 changes: 69 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,72 @@ And add `JWTMiddleware` to all the target dependency arrays you want to access t

Complete the installation by running `vapor update` or `swift package update`.

## Middleware

**`JWTAuthenticatableMiddlware`**

Handles authenticating/authorizing a model conforming to `JWTAuthenticatable` using data pulled from a request.

```swift
route.group(JWTAuthenticatableMiddlware<User>())
```

---

**`JWTVerificationMiddleware`**

Gets a JWT payload from a request, validates it, and stores it for later.

```swift
route.group(JWTVerificationMiddleware<UserPayload>())
```

## Protocols

See source doc comments for details.

- `IdentifiableJWTPayload`
- `JWTAuthenticatable`
- `BasicJWTAuthenticatable`
## Modules

There are currently 2 modules in the JWTMiddleware package; `JWTMiddleware` and `JWTAuthenticatable`.

The `JWTMiddleware` module contains middleware for request authentication/authorization and helpers for getting data stored in the request by the middleware.

The `JWTAuthenticatable` module holds protocols that allow a type to be authenticated/authorized in the middleware declared in the `JWTMiddleware` module.

## JWTMiddleware

The JWTMiddleware module exports the following types:

- `JWTAuthenticatableMiddlware`:

Handles authenticating/authorizing a model conforming to `JWTAuthenticatable` using data pulled from a request.

When a request is passed though the middleware, it will first check to see if the specified model is already authenticated. If it is not, it will try to get data to authenticate the model by calling the static `authBody(from:)` method. If it successfully authenticates, it will get the model's access token by calling `accessToken(on:)`, then store both the token and the authenticated model in the request for accessing later. If `nil` is return from `authBody(from:)`, then we try to authenticate using data from the `Authorization: Bearer ...` header. If authentication succeeds, we will store both the token fetched from the header and the authenticated model in the request.

You can register the middleware to a route group as shown below.

```swift
let auth = route.group(JWTAuthenticatableMiddlware<User>())
```

- `JWTVerificationMiddleware`:

Gets the value from the `Authorization: Bearer ...` header, verifies it with the specified payload type, and stores it in the request for later.

```swift
route.group(JWTVerificationMiddleware<UserPayload>())
```

## JWTAuthenticatable

The JWTAuthenticatable module exports the following types:

- `IdentifiableJWTPayload`:

Represents a JWT payload with an `id` value. This is used by the `BasicJWTAuthenticatable` to access a model from the database based on its `id` property.

- `JWTAuthenticatable`:

A model that can be authorized with a JWT payload and authenticated with an unspecified type that is defined by the implementing type.

This protocol requires the following types/properties/methods:

- `associatedtype AuthBody`: Used for authentication of the model.
- `associatedtype Payload: IdentifiableJWTPayload`: A type that the payload of a JWT token can be converted to. This type is used to authorize requests.

- `accessToken(on request: Request) throws -> Future<Payload>`: This method should create a payload for a JWT token that will later be used to authorize the model.
- `static authBody(from request: Request)throws -> AuthBody?`: Gets data from a request that can be used to authenticate a model
- `static authenticate(from payload: Payload, on request: Request)throws -> Future<Self>`: Verifies the payload passed in and gets an instance of the model based on the payload's contents.
- `static authenticate(from body: AuthBody, on request: Request)throws -> Future<Self>`: Gets a model and checks it against the contents of the `body` parameter passed in.

- `BasicJWTAuthenticatable`:

Implements `JWTAuthenticatable` methods for authenticating with an id/password combination. The ID should either be a username or email.

The `authBody` method implementation gets the name of the property referenced by the `usernameKey` key-path. It will then extract the values from the request body with the key from `usernameKey` and `"password"`.

The payload authorization method simply finds the instance of the model stored in the database with an ID equal to the `id` property in the payload.

The `AuthBody` authentication method fetches the first user from the database with a `usernameKey` property equal to the `body.username` value. It then checks to see if the model's password hash is equal to the `body.password` value, using BCrypt verification.

This protocols requires the following type/properties:

- `associatedtype Payload: IdentifiableJWTPayload`: A type that the payload of a JWT token can be converted to. This type is used to authorize requests.
- `usernameKey: KeyPath<Self, String>`: The key-path for the property that will be checked against the `body.username` value during authentication. This will usually be either an email or username. This property can be either a variable or constant.
- `var password: String` The hashed password of the model, used to verify the request'c credibility. This properties value _must_ be hash using BCrypt.
2 changes: 1 addition & 1 deletion Sources/JWTAuthenticatable/BasicJWTAuthenticatable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ extension BasicJWTAuthenticatable {
public static func authenticate(from payload: Payload, on request: Request)throws -> Future<Self> {

// Fetch the model from the database that has an ID
// matching the one in the JWET payload.
// matching the one in the JWT payload.
return try Self.find(payload.id, on: request)

// No user was found. Throw a 404 (Not Found) error
Expand Down
2 changes: 1 addition & 1 deletion Sources/JWTAuthenticatable/JWTAuthenticatable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public protocol JWTAuthenticatable: Authenticatable, Content where Payload.ID ==
///
/// - Returns: A future value of the model that was fetched.
/// - Throws: Whatever the implementation throws.
static func authenticate(from body: AuthBody, on reques: Request)throws -> Future<Self>
static func authenticate(from body: AuthBody, on request: Request)throws -> Future<Self>
}


2 changes: 1 addition & 1 deletion Sources/JWTMiddleware/JWTAuthenticatableMiddlware.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import JWTAuthenticatable
@_exported import JWTAuthenticatable
import JWTVapor
import Fluent
import Crypto
Expand Down

0 comments on commit dd47e20

Please sign in to comment.