heroes
application lets anyone create or view the same set of heroes. We will continue to build on the last chapter's project, heroes
, requiring a user to log in before viewing or creating heroes.curl
, conduit document client
and tests.Authorization
header. There are a number of security risks involved in doing this, so OAuth 2.0 takes another approach: you send your credentials once, and get a 'access token' in return. You then send this access token in each request. Because the server grants the token, it knows that you've already entered your credentials (you've authenticated) and it remembers who the token belongs to. It's effectively the same thing as sending your credentials each time, except that the token has a time limit and can be revoked when things go wrong.conduit
package, but it is a separate library named conduit/managed_auth
. It takes a few steps to set up that might be difficult to understand if you are not familiar with OAuth 2.0, but you'll get a well-tested, secure authorization implementation.model/user.dart
and enter the following code:package:conduit/managed_auth.dart
contains types that use the ORM to store users, tokens and other OAuth 2.0 related data. One of those types is ResourceOwnerTableDefinition
, the superclass of our user's table definition. This type contains all of the required fields that Conduit needs to implement authentication.ResourceOwnerTableDefinition
looks like this:User
's table definition, our User
table has all of these database columns.User
implements ManagedAuthResourceOwner<_User>
- this is a requirement of any OAuth 2.0 resource owner type when using package:conduit/managed_auth
.AuthServer
. This type has all of the logic needed to authentication and authorize users. For example, an AuthServer
can generate a new token if given valid user credentials.channel.dart
, add the following imports to the top of your file:authServer
property in your channel and initialize it in prepare
:AuthServer
handles the logic of authentication and authorization, it doesn't know how to store or fetch the data it uses for those tasks. Instead, it relies on a delegate object to handle storing and fetching data from a database. In our application, we use ManagedAuthDelegate<T>
- from package:conduit/managed_auth
- as the delegate. This type uses the ORM for these tasks; the type argument must be our application's user object.conduit/managed_auth
, we've added a few more managed objects to our application (to store tokens and other authentication data) and we also have a new User
managed object. It's a good time to run a database migration. From your project directory, run the following commands:POST
requests that contain a username and password in the body. It will insert a new user into the database and securely hash the user's password.User
object, it needs a password field, but we don't want to store the password in the database without first hashing it.Serialize
annotation to it. Add this property to your User
type:User
has a transient property password
that can be read on input (from a request body), but is not sent on output (to a response body). We don't have to run a database migration because transient properties are not stored in a database.controller/register_controller.dart
and enter the following code:channel.dart
, let's link this controller - don't forget to import it!curl
from the command-line. (We'll specify -n1
to designate using one isolate and speed up startup.)entryPoint
in channel.dart
to add an AuthController
for the route /auth/token
:AuthController
follows the OAuth 2.0 specification for granting access tokens when given valid user credentials. To understand how a request to this endpoint must be structured, we need to discuss OAuth 2.0 clients. In OAuth 2.0, a client is an application that is allowed to access your server on behalf of a user. A client can be a browser application, a mobile application, another server, a voice assistant, etc. A client always has an identifier string, typically something like 'com.conduit.dart.account_app.mobile'.conduit auth add-client
CLI. Run the following command from your project directory:Authorization
header.grant_type=password
is included in the request bodyapplication/x-www-form-urlencoded
; this means the request body is effectively a query string (e.g. username=bob&password=pw&grant_type=password
)curl
:channel.dart
, link an Authorizer
in the middle of the /heroes
channel:Authorizer
protects a channel from unauthorized requests by validating the Authorization
header of a request. When created with Authorizer.bearer
, it ensures that the authorization header contains a valid access token. Restart your application and try and access the /heroes
endpoint without including any authorization:Authorizer
can validate access token scopes and basic authorization credentials. You'll see examples of these in a later exercise.