mirror of https://github.com/vapor/docs.git
more auth
This commit is contained in:
parent
d50c7d7433
commit
9ba7527bcd
|
|
@ -1,6 +1,83 @@
|
|||
---
|
||||
currentMenu: auth-middleare
|
||||
currentMenu: auth-middleware
|
||||
---
|
||||
|
||||
# Auth Middleware
|
||||
# Middleware
|
||||
|
||||
`AuthMiddleware` is at the core of adding authorization to your project. It is responsible for initializing dependencies, checking credentials, and handling sessions.
|
||||
|
||||
## Create
|
||||
|
||||
Once you have something that conforms to `Auth.User`, you can create an `AuthMiddleware`. Let's assume we have a class `User` that conforms to `Auth.User`.
|
||||
|
||||
> Note: You may need to include a module name before `User` to disambiguate.
|
||||
|
||||
```swift
|
||||
import Auth
|
||||
|
||||
let auth = AuthMiddleware(user: User.self)
|
||||
```
|
||||
|
||||
Creating the `AuthMiddleware` can be that simple, or you can customize it with additional initialization arguments.
|
||||
|
||||
### Cookie
|
||||
|
||||
Customize the type of cookie the `AuthMiddleware` creates by passing a `CookieFactory`.
|
||||
|
||||
```swift
|
||||
let auth = AuthMiddleware(user: User.self) { value in
|
||||
return Cookie(
|
||||
name: "vapor-auth",
|
||||
value: value,
|
||||
expires: Date().addingTimeInterval(60 * 60 * 5), // 5 hours
|
||||
secure: true,
|
||||
httpOnly: true
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Cache
|
||||
|
||||
A custom `CacheProtocol` can be passed as well. The `MemoryCache()` used by default is not persisted between server restarts and does not allow for sharing between multiple running instances.
|
||||
|
||||
```swift
|
||||
import VaporRedis
|
||||
|
||||
let redis = RedisCache()
|
||||
let auth = AuthMiddleware(user: User.self, cache: redis)
|
||||
```
|
||||
|
||||
> Note: This example uses the [redis-provider](https://github.com/vapor/redis-provider) package.
|
||||
|
||||
### Realm
|
||||
|
||||
To customize the `AuthMiddleware` even further, you can use a custom `Realm`. The `Realm` takes the responsibility of registering and authenticating the user away from the `Auth.User` protocol.
|
||||
|
||||
```swift
|
||||
let facebook = FacebookRealm()
|
||||
let auth = AuthMiddleware(user: User.self, realm: facebook)
|
||||
```
|
||||
|
||||
> Note: `FacebookRealm` is hypothetical.
|
||||
|
||||
## Add
|
||||
|
||||
Once you've created the `AuthMiddleware`, you can add it to the `Droplet`.
|
||||
|
||||
```swift
|
||||
let drop = Droplet(availableMiddleware: ["auth": auth])
|
||||
```
|
||||
|
||||
Once you've added the `AuthMiddleware` to the available middleware dictionary, make sure to enable it in your [middleware.json](../guide/middleware.md) configuration file.
|
||||
|
||||
### Sharing Cache
|
||||
|
||||
If you'd like the `Droplet` and the `AuthMiddleware` to share the same `CacheProtocol`, initialize it earlier and pass it to both.
|
||||
|
||||
```
|
||||
import VaporRedis
|
||||
|
||||
let redis = RedisCache()
|
||||
let auth = AuthMiddleware(user: User.self, cache: redis)
|
||||
let drop = Droplet(cache: redis, availableMiddleware: ["auth": auth])
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,5 +2,32 @@
|
|||
currentMenu: auth-protect
|
||||
---
|
||||
|
||||
# Protect Middleware
|
||||
# Protect
|
||||
|
||||
Once the `AuthMiddleware` has been enabled, you can use `ProtectMiddleware` to prevent certain routes from being accessed without authorization.
|
||||
|
||||
## Create
|
||||
|
||||
To create a `ProtectMiddleware`, you must give it the error to throw in case authorization fails.
|
||||
|
||||
```swift
|
||||
let error = Abort.custom(status: .forbidden, message: "Invalid credentials.")
|
||||
let protect = ProtectMiddleware(error: error)
|
||||
```
|
||||
|
||||
Here we pass it a simple 403 response.
|
||||
|
||||
## Route Group
|
||||
|
||||
Once the middleware has been created, you can add it to route groups. Learn more about middleware and routing in [route groups](../routing/group.md).
|
||||
|
||||
```
|
||||
drop.grouped(protect).group("secure") { secure in
|
||||
secure.get("about") { req in
|
||||
let user = try req.user()
|
||||
return user
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Visiting `GET secure/about` will return the authorized user, or an error if no user is authorized.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
---
|
||||
currentMenu: auth-request
|
||||
---
|
||||
|
||||
# Request
|
||||
|
||||
The `auth` property on `Request` let's you authenticate users and also provides some convenience methods for accessing common authorization headers.
|
||||
|
||||
## Authorization
|
||||
|
||||
The authorization header is a great place to send credentials from a client.
|
||||
|
||||
```
|
||||
Authorization: xxxxxxxxxx
|
||||
```
|
||||
|
||||
You can access the authorization header through `req.auth.header`. Two common patterns are basic and bearer.
|
||||
|
||||
### Basic
|
||||
|
||||
Basic authorization consists of a username and password concatenated into a string and base64 encoded.
|
||||
|
||||
```
|
||||
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
|
||||
```
|
||||
|
||||
Here is what an example header looks like. You can read more about basic auth on [wikipedia](https://en.wikipedia.org/wiki/Basic_access_authentication).
|
||||
|
||||
```swift
|
||||
guard let credentials = req.auth.header?.basic else {
|
||||
throw Abort.badRequest
|
||||
}
|
||||
```
|
||||
|
||||
The basic header returns an `APIKey` credential.
|
||||
|
||||
```
|
||||
class APIKey: Credentials {
|
||||
let id: String
|
||||
let secret: String
|
||||
}
|
||||
```
|
||||
|
||||
### Bearer
|
||||
|
||||
Another common method is bearer which consists of a single API key.
|
||||
|
||||
```
|
||||
Authorization: Bearer apikey123
|
||||
```
|
||||
|
||||
It is accessed similarly to the basic header and returns an `AccessToken` credential.
|
||||
|
||||
```
|
||||
class AccessToken: Credentials {
|
||||
let string: String
|
||||
}
|
||||
```
|
||||
|
||||
### Raw
|
||||
|
||||
To access the raw authorization header, use `req.auth.header?.header`.
|
||||
|
||||
## Credentials
|
||||
|
||||
Both Basic and Bearer return something that conforms to `Credentials`. You can always create a custom `Credentials` object for authorization by conforming your own class to `Credentials` or by manually creating an `APIKey` or `AccessToken`.
|
||||
|
||||
```swift
|
||||
let key = AccessToken(string: "apikey123")
|
||||
```
|
||||
|
||||
## Login
|
||||
|
||||
Once you have some object that conforms to `Credentials`, you can try to login the user.
|
||||
|
||||
```swift
|
||||
try req.auth.login(credentials)
|
||||
```
|
||||
|
||||
If this call succeeds, the user is logged in and a session has been started. They will stay logged in as long as their cookie is valid.
|
||||
|
||||
### Authenticate
|
||||
|
||||
Logging in calls the `authenticate` method on the `Realm`. If you have simply passed an `Auth.User` conformer to the `AuthMiddleware`, then this will call the `authenticate` method on that type.
|
||||
|
||||
The method will be passed whichever credentials you are trying to login with, so make sure you add support for all the credential types you may want to use on your `Auth.User`.
|
||||
|
||||
### Identifier
|
||||
|
||||
One important credential type is the `Identifier` type. This is used by Vapor when fetching the `User` object from the authorization cookie. It is also a convenient way to log a user in manually.
|
||||
|
||||
```swift
|
||||
static func authenticate(credentials: Credentials) throws -> Auth.User {
|
||||
if ... {
|
||||
...
|
||||
} else if let id = credentials as? Identifier {
|
||||
guard let user = try User.find(id.id) else {
|
||||
throw Abort.custom(status: .badRequest, message: "Invalid identifier.")
|
||||
}
|
||||
|
||||
return user
|
||||
} else {
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Adding the `Identifier` case for `Credentials` is easy, just look up the user by the identifier.
|
||||
|
||||
```swift
|
||||
let id = Identifier(id: 42)
|
||||
try req.auth.log(id)
|
||||
```
|
||||
|
||||
Now you can manually log users in with just their identifiers.
|
||||
|
||||
### Ephemeral
|
||||
|
||||
If you just want to log the user in for a single request, disable persistance.
|
||||
|
||||
```swift
|
||||
req.auth.login(credentials, persist: false)
|
||||
```
|
||||
|
||||
## User
|
||||
|
||||
By default, `request.auth.user()` returns the authorized `Auth.User`. This will need to be casted to your internal user type if you want to access its values.
|
||||
|
||||
Adding a convenience method on `Request` is a great way to simplify this.
|
||||
|
||||
```swift
|
||||
extension Request {
|
||||
func user() throws -> User {
|
||||
guard let user = try auth.user() as? User else {
|
||||
throw Abort.custom(status: .badRequest, message: "Invalid user type.")
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now you can get access to your `User` type with `try req.user()`.
|
||||
|
|
@ -53,7 +53,8 @@ static func authenticate(credentials: Credentials) throws -> Auth.User {
|
|||
|
||||
return user
|
||||
} else {
|
||||
throw Abort.custom(status: .forbidden, message: "Unsupported credentials.")
|
||||
let type = type(of: credentials)
|
||||
throw Abort.custom(status: .forbidden, message: "Unsupported credential type: \(type).")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -64,6 +65,10 @@ Once we have the access token, we will use it to query the `User` model for an e
|
|||
|
||||
Once we have found the user associated with the supplied access token, we simply return it.
|
||||
|
||||
#### Identifier
|
||||
|
||||
Vapor uses the `Identifier` credential type internally to lookup users from sessions. You can read more in the [Request](request.md) section.
|
||||
|
||||
### Register
|
||||
|
||||
Similar to the authenticate method, the register method takes credentials. But instead of fetching the user from the data store, it provides a convenient way to create the user. You are not required to register your users through this method.
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ menu:
|
|||
auth-middleware:
|
||||
text: Middleware
|
||||
relativeUrl: auth/middleware.html
|
||||
auth-request:
|
||||
text: Request
|
||||
relativeUrl: auth/request.html
|
||||
auth-protect:
|
||||
text: Protect
|
||||
relativeUrl: auth/protect.html
|
||||
|
|
|
|||
Loading…
Reference in New Issue