From 39f5c4a75341e29a957910cb9f044797f9540ea2 Mon Sep 17 00:00:00 2001 From: Alex Patterson <139158618+alex-fusionauth@users.noreply.github.com> Date: Tue, 10 Oct 2023 18:23:59 -0400 Subject: [PATCH] Quickstart ruby on rails api (#2528) * copy over * update ruby on rails api * remove direct link * pr updates * pr fixes * use cookies * add redirects * remove old * remove rails api from example apps * updated based on pr suggestions * correct ports * stomped on redirect * add quickstart to exampleapps * update all with json --- .../quickstart-ruby-on-rails-api.mdx | 404 ++++++++++++++++++ .../docs/quickstarts/quickstart-sections.ts | 7 - site/_data/exampleapps.yaml | 2 +- site/_layouts/doc.liquid | 2 +- .../tutorials/integrate-ruby-rails-api.md | 199 --------- 5 files changed, 406 insertions(+), 208 deletions(-) create mode 100644 astro/src/content/quickstarts/quickstart-ruby-on-rails-api.mdx delete mode 100644 site/docs/v1/tech/tutorials/integrate-ruby-rails-api.md diff --git a/astro/src/content/quickstarts/quickstart-ruby-on-rails-api.mdx b/astro/src/content/quickstarts/quickstart-ruby-on-rails-api.mdx new file mode 100644 index 0000000000..59f87c0d29 --- /dev/null +++ b/astro/src/content/quickstarts/quickstart-ruby-on-rails-api.mdx @@ -0,0 +1,404 @@ +--- +title: Ruby on Rails +description: Quickstart Integration of a Ruby on Rails API with FusionAuth +navcategory: getting-started +prerequisites: Ruby, bundler and Rails +section: api +technology: Ruby on Rails +language: Ruby +icon: /img/icons/ruby-on-rails.svg +faIcon: fa-gem +color: red +cta: EmailListCTA +coderoot: https://raw.githubusercontent.com/FusionAuth/fusionauth-quickstart-ruby-on-rails-api/main +--- +import Aside from '../../components/Aside.astro'; +import Diagram from '../../diagrams/quickstarts/resource-server.astro'; +import RemoteCode from '../../components/RemoteCode.astro'; + +In this article, you are going to learn how to integrate a Ruby on Rails API with FusionAuth. You will protect an API resource from unauthorized usage. You'll be building it for [ChangeBank](https://www.youtube.com/watch?v=CXDxNCzUspM), a global leader in converting dollars into coins. + +The docker compose file and source code for a complete application are available at https://github.com/FusionAuth/fusionauth-quickstart-ruby-on-rails-api. + +## Prerequisites + +- [Ruby 2.7.x](https://rubyonrails.org/): This quickstart was built using Ruby 2.7. This example may work on different versions of Rails, but it has not been tested. +- [Rails 7.0.x.x](https://rubyonrails.org/): This quickstart was built using Rails 7.0.7.2. This example may work on different versions of Rails, but it has not been tested. +- [SQLite](https://www.sqlite.org/download.html): This quickstart was built using sqlite3 (please note MacOS comes with sqlite3). +- [Docker](https://www.docker.com): The quickest way to stand up FusionAuth. Ensure you also have [docker compose](https://docs.docker.com/compose/) installed. +- (Alternatively, you can [Install FusionAuth Manually](/docs/v1/tech/installation-guide/)). + +## General Architecture + +A client wants access to an API resource at `/resource`. However, it is denied this resource until it acquires an access token from FusionAuth. + + + +While the access token is acquired via the Login API above, this is for simplicity of illustration. The token can be, and typically is, acquired through one of the [OAuth grants](/docs/v1/tech/oauth/). + +## Getting Started + +In this section, you’ll get FusionAuth up and running and create a resource server which will serve the API. + +### Clone The Code + +First off, grab the code from the repository and change into that directory. + +``` +git clone https://github.com/FusionAuth/fusionauth-quickstart-ruby-on-rails-api +cd fusionauth-quickstart-ruby-on-rails-api +``` + +### Run FusionAuth Via Docker + +In the root directory of the repo you’ll find a Docker compose file (docker-compose.yml) and an environment variables configuration file (.env). Assuming you have Docker installed on your machine, you can stand up FusionAuth up on your machine with: + +``` +docker compose up -d +``` + +Here you are using a bootstrapping feature of FusionAuth, called [Kickstart](/docs/v1/tech/installation-guide/kickstart). When FusionAuth comes up for the first time, it will look at the `kickstart/kickstart.json` file and configure FusionAuth to a certain initial state. + + + +FusionAuth will be initially configured with these settings: + +* Your client Id is: `e9fdb985-9173-4e01-9d73-ac2d60d1dc8e` +* Your client secret is: `super-secret-secret-that-should-be-regenerated-for-production` +* Your example teller username is `teller@example.com` and your password is `password`. They will have the role of `teller`. +* Your example customer username is `customer@example.com` and your password is `password`. They will have the role of `customer`. +* Your admin username is `admin@example.com` and your password is `password`. +* Your fusionAuthBaseUrl is 'http://localhost:9011/' + +You can log into the [FusionAuth admin UI](http://localhost:9011/admin) and look around if you want, but with Docker/Kickstart you don't need to. + +## Create Your Ruby on Rails Resource Server Application + +Now you are going to create a Ruby on Rails API application. While this section builds a simple API, you can use the same configuration to integrate an existing API with FusionAuth. + +We are going to be building an API backend for a banking application called ChangeBank. This API will have two endpoints: +- `make-change`: This endpoint will allow you to call GET with a `total` amount and receive a response indicating how many nickels and pennies are needed to make change. Valid roles are `customer` and `teller`. +- `panic`: Tellers may call this endpoint to call the police in case of an incident. The only valid role is `teller`. + +Both endpoints will be protected such that a valid JSON web token (JWT) will be required in the `Authorization` header in order to be accessed. Additionally, the JWT must have a `roles` claim containing the appropriate role to use the endpoint. + + + +### Initialize The Application + +Initialize the Ruby on Rails application using the following: + +``` +rails new your-application --api +cd your-application +``` + +Once this is complete, you will see a new directory called `your-application` with several sub directories. + +You need to add the following lines to the `Gemfile` in the application root directory to include two new dependencies: + +```shell +gem 'rack-jwt', git: 'https://github.com/FusionAuth/rack-jwt' +gem 'dotenv-rails' +``` + +Your full `Gemfile` will now look like + + + +Then, install these new gems, by issuing the following command in your terminal window. + +```shell +bundle install +``` +### Update Routes Config + +Update `/config/routes.rb` to include the two new routes that will be created for `/make-change` and `/panic`. Your `routes.rb` file should now match the below code. + + + +### Add Security + +Create a new file to hold your environment variables directly in the `your-application` directory called `.env.development`. You will need to add two variables that are used to call your FusionAuth instance `FUSIONAUTH_LOCATION` and `CLIENT_ID`, with the values that match below. + +``` +# for rails + +FUSIONAUTH_LOCATION=http://localhost:9011 + +CLIENT_ID=e9fdb985-9173-4e01-9d73-ac2d60d1dc8e +``` + +Utilizing the enviroment variables you just added, you can now setup JSON Web Token (JWT) based authentication. Create a new file `config/initializers/jwt_rack.rb`. This initializer is used to hold configuration settings that are made after all of the frameworks and plugins are loaded. + + + +Having this code protects your endpoints from anonymous users and passes the JWT payload to the controller. The JWT payload includes `roles` encoded in the JWT you receive from FusionAuth. The decoded payload of a JWT for a `teller` might look like this: + +```json +{ + "aud": "e9fdb985-9173-4e01-9d73-ac2d60d1dc8e", + "exp": 1689289585, + "iat": 1689285985, + "iss": "http://localhost:9011", + "sub": "00000000-0000-0000-0000-111111111111", + "jti": "ebaa4184-2320-47dd-925b-2e18756c635f", + "authenticationType": "PASSWORD", + "email": "teller@example.com", + "email_verified": true, + "applicationId": "e9fdb985-9173-4e01-9d73-ac2d60d1dc8e", + "roles": [ + "teller" + ], + "auth_time": 1689285985, + "tid": "d7d09513-a3f5-401c-9685-34ab6c552453" +} +``` + +### Add Controllers + +Create a new file for the MakeChangeController `app/controllers/make_change_controller.rb`. This controller will verify that the authenticated user has either the `teller` or `customer` role. It then takes in the url parameter `total` to calculate which coins will be returned in the JSON payload. + + + +Create a new file for the PanicController `app/controllers/panic_controller.rb`. This controller will verify that the authenticated user has only the `teller` role. It will respond with `Proper role not found for user.` if this role is not found. This route also verifies that this is a `POST` request and if this is not true it will respond with `Only POST method is supported.` If both of these tests are passed the controller will return a successful message of `We've called the police!`. + + + +## Run the API + +Start the API resource server by running: + +In a command shell window, navigate to the root directory of your application and run the following command to start the server. + +```shell +bundle e rails s -p 4001 +``` + +### Get a Token + +There are [several ways to acquire a token](/docs/v1/tech/oauth/) in FusionAuth, but for this example you will use the [Login API](/docs/v1/tech/apis/login) to keep things simple. + + + +First let's try the requests as the `teller@example.com` user. Based on the configuration this user has the `teller` role and should be able to use both `/make-change` and `/panic`. + +Acquire an access token for `teller@example` by making the following request + +```shell +curl --location 'http://localhost:9011/api/login' \ +--header 'Authorization: this_really_should_be_a_long_random_alphanumeric_value_but_this_still_works' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "loginId": "teller@example.com", + "password": "password", + "applicationId": "e9fdb985-9173-4e01-9d73-ac2d60d1dc8e" +}' +``` +Your response should look like the below JSON. Grab the token field (which begins with ey). + +```json +{ + "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InVOYl9iQzFySHZZTnZMc285VzRkOEprZkxLWSJ9.eyJhdWQiOiJlOWZkYjk4NS05MTczLTRlMDEtOWQ3My1hYzJkNjBkMWRjOGUiLCJleHAiOjE2ODkzNTMwNTksImlhdCI6MTY4OTM1Mjk5OSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo5MDExIiwic3ViIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMTExMTExMTExMTExIiwianRpIjoiY2MzNWNiYjUtYzQzYy00OTRjLThmZjMtOGE4YWI1NTI0M2FjIiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6InRlbGxlckBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhcHBsaWNhdGlvbklkIjoiZTlmZGI5ODUtOTE3My00ZTAxLTlkNzMtYWMyZDYwZDFkYzhlIiwicm9sZXMiOlsiY3VzdG9tZXIiLCJ0ZWxsZXIiXSwiYXV0aF90aW1lIjoxNjg5MzUyOTk5LCJ0aWQiOiJkN2QwOTUxMy1hM2Y1LTQwMWMtOTY4NS0zNGFiNmM1NTI0NTMifQ.WLzI9hSsCDn3ZoHKA9gaifkd6ASjT03JUmROGFZaezz9xfVbO3quJXEpUpI3poLozYxVcj2hrxKpNT9b7Sp16CUahev5tM0-4_FaYlmUEoMZBKo2JRSH8hg-qVDvnpeu8nL6FXxJII0IK4FNVwrQVFmAz99ZCf7m5xruQSziXPrfDYSU-3OZJ3SRuvD8bMopSiyRvZLx6YjWfBsvGSmMXeh_8vHG5fVkq5w1IkaDdugHnivtJIivHuCfl38kQBgw9rAqJLJoKRHHW0Ha7vHIcS6OCWWMDIIVspLyQNcLC16pL9Nss_5v9HMofow1OvQ9sUSMrbbkipjKq2peSjG7qA", + "tokenExpirationInstant": 1689353059670, + "user": { + ... + } +} +``` + +### Make the Request + + + +Make a request to `/make-change` with a query parameter `total=1.02`. Use the `token` as the `app.at` cookie. + +```shell +curl 'http://localhost:4001/make-change?total=1.02' \ +--cookie 'app.at=' +``` + +Alternatively you can make the same request by passing your token in the `Authorization` header. + +```shell +curl --location 'http://localhost:4001/make-change?total=1.02' \ +--header 'Authorization: Bearer ' +``` + +Your response should look like this: + +```json +{ + "Message": "We can make change using 20 quarters 1 dimes 0 nickels 2 pennies", + "Change": [ + { + "Denomination": "quarters", + "Count": 20 + }, + { + "Denomination": "dimes", + "Count": 1 + }, + { + "Denomination": "nickels", + "Count": 0 + }, + { + "Denomination": "pennies", + "Count": 2 + } + ] +} +``` + +You were authorized, success! You can try making the request without the `--cookie` or with a different string rather than a valid token, and see that you are denied access. + +Next call the `/panic` endpoint because you are in trouble! + +```shell +curl --location --request POST 'http://localhost:4001/panic' \ +--cookie 'app.at=' +``` + +This is a `POST` not a `GET` because you want all your emergency calls to be non-idempotent. + +Your response should look like this: + +```json +{"message":"We've called the police!"} +``` + +Nice, help is on the way! + +Now let's try as `customer@example.com` who has the role `customer`. Acquire a token for `customer@example.com`. + +```shell +curl --location 'http://localhost:9011/api/login' \ +--header 'Authorization: this_really_should_be_a_long_random_alphanumeric_value_but_this_still_works' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "loginId": "customer@example.com", + "password": "password", + "applicationId": "e9fdb985-9173-4e01-9d73-ac2d60d1dc8e" +}' +``` + +Your response should look like the below JSON. Grab the token field (which begins with ey). + +```json +{ + "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InVOYl9iQzFySHZZTnZMc285VzRkOEprZkxLWSJ9.eyJhdWQiOiJlOWZkYjk4NS05MTczLTRlMDEtOWQ3My1hYzJkNjBkMWRjOGUiLCJleHAiOjE2ODkzNTQxMjMsImlhdCI6MTY4OTM1MzUyMywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo5MDExIiwic3ViIjoiMDAwMDAwMDAtMDAwMC0wMDAwLTAwMDAtMjIyMjIyMjIyMjIyIiwianRpIjoiYjc2YWMwMGMtMDdmNi00NzkzLTgzMjgtODM4M2M3MGU4MWUzIiwiYXV0aGVudGljYXRpb25UeXBlIjoiUEFTU1dPUkQiLCJlbWFpbCI6ImN1c3RvbWVyQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImFwcGxpY2F0aW9uSWQiOiJlOWZkYjk4NS05MTczLTRlMDEtOWQ3My1hYzJkNjBkMWRjOGUiLCJyb2xlcyI6WyJjdXN0b21lciJdLCJhdXRoX3RpbWUiOjE2ODkzNTM1MjMsInRpZCI6ImQ3ZDA5NTEzLWEzZjUtNDAxYy05Njg1LTM0YWI2YzU1MjQ1MyJ9.T1bELQ6a_ItOS0_YYpvqhIVknVMNeamcoC7BWnPjg2lgA9XpCmFA2mVnycoeuz-mSOHbp2cCoauP5opxehBR2lCn4Sz0If6PqgJgXKEpxh5pAxCPt91UyfjH8hGDqE3rDh7E4Hqn7mb-dFFwdfX7CMdKvC3dppMbXAGCZTl0LizApw5KIG9Wmt670339pSf5lzD38P9WAG5Wr7fAmVrIJPVu6yv2FoR-pMYD2lnAF63HWKknrWB-khmhr9ZKRLXKhP1UK-ThY1FSnmpp8eNblsBqCxf6WaYxYkdp5_F2e56M4sQwHzrg4P9tZGVCmMri4dShF3Ck7OGa7hel-iIPew", + "tokenExpirationInstant": 1689354123118, + "user": { + ... + } +} +``` + +Now use that token to call `/make-change` with a query parameter `total=3.24` + +```shell +curl --location 'http://localhost:4001/make-change?total=3.24' \ +--cookie 'app.at=' +``` + +Your response should look like this: + +```json +{ + "Message": "We can make change using 12 quarters 2 dimes 0 nickels 4 pennies", + "Change": [ + { + "Denomination": "quarters", + "Count": 12 + }, + { + "Denomination": "dimes", + "Count": 2 + }, + { + "Denomination": "nickels", + "Count": 0 + }, + { + "Denomination": "pennies", + "Count": 4 + } + ] +} +``` + +So far so good. Now let's try to call the `/panic` endpoint. (We're adding the `-i` flag to see the headers of the response) + +```shell +curl -i --request POST 'http://localhost:4001/panic' \ +--cookie 'app.at=' +``` + +You will get a `401 Unauthorized` response with the following message. + +```json +{"message":"Proper role not found for user"} +``` + +Uh oh, I guess you are not allowed to do that. + +Enjoy your secured resource server! + +## Next Steps + +This quickstart is a great way to get a proof of concept up and running quickly, but to run your API in production, there are some things you're going to want to do. + +### FusionAuth Integration + +* Rather than call the Login API, you're probably going to want to use the [Authorization Code grant](/docs/v1/tech/oauth/#example-authorization-code-grant), which keeps all sensitive credentials within the bounds of FusionAuth. You can customize the [hosted login pages](/docs/v1/tech/themes/). +* You may want to generate a token using the [Client Credentials grant](/docs/v1/tech/oauth/#example-client-credentials-grant) if you are calling the API from another service. + +### Security +* Customize the [token expiration times and policies](/docs/v1/tech/oauth/#configure-application-oauth-settings) in FusionAuth +* [Make sure you know how to securely consume a token](/articles/tokens/building-a-secure-jwt#consuming-a-jwt) +* Secure your API [using an API gateway](/docs/v1/tech/developer-guide/api-gateways/) rather than at the framework layer. + +## Troubleshooting + +* I get `This site can’t be reached localhost refused to connect.` when I call the Login API. + +Ensure FusionAuth is running in the Docker container. You should be able to login as the admin user, `admin@example.com` with a password of `password` at [http://localhost:9011/admin](http://localhost:9011/admin). + +* The `/panic` endpoint doesn't work when I call it. + +Make sure you are making a `POST` call and using a token with the `teller` role. + +* It still doesn't work + +You can always pull down a complete running application and compare what's different. + +``` +git clone https://github.com/FusionAuth/fusionauth-quickstart-ruby-on-rails-api.git +cd fusionauth-quickstart-ruby-on-rails-api +docker-compose up -d +cd complete-application +bundle install +bundle e rails s -p 4001 +``` diff --git a/astro/src/pages/docs/quickstarts/quickstart-sections.ts b/astro/src/pages/docs/quickstarts/quickstart-sections.ts index e8205ea6ee..9ab13257f6 100644 --- a/astro/src/pages/docs/quickstarts/quickstart-sections.ts +++ b/astro/src/pages/docs/quickstarts/quickstart-sections.ts @@ -112,13 +112,6 @@ const qsSections: QuickStartSection[] = [ faIcon: 'fa-hashtag', navColor: 'blue', }, - { - href: '/docs/v1/tech/tutorials/integrate-ruby-rails-api', - title: 'Ruby on Rails', - icon: '/img/icons/ruby-on-rails.svg', - faIcon: 'fa-gem', - navColor: 'red', - }, { href: '/docs/v1/tech/tutorials/integrate-express-api', title: 'Express', diff --git a/site/_data/exampleapps.yaml b/site/_data/exampleapps.yaml index 823429ded0..8099757e27 100644 --- a/site/_data/exampleapps.yaml +++ b/site/_data/exampleapps.yaml @@ -39,7 +39,7 @@ name: Javascript JWT description: JWT creation and decoding examples with javascript language: javascript -- url: https://github.com/fusionauth/fusionauth-example-rails-api-guide +- url: https://github.com/FusionAuth/fusionauth-quickstart-ruby-on-rails-api name: Rails apis description: Protecting a Rails API with a JWT language: ruby diff --git a/site/_layouts/doc.liquid b/site/_layouts/doc.liquid index cadf0cf710..839e350e45 100644 --- a/site/_layouts/doc.liquid +++ b/site/_layouts/doc.liquid @@ -90,7 +90,7 @@
  • Python Flask
  • React
  • Ruby on Rails
  • -
  • Ruby on Rails API
  • +
  • Ruby on Rails API
  • Vue.js
  • diff --git a/site/docs/v1/tech/tutorials/integrate-ruby-rails-api.md b/site/docs/v1/tech/tutorials/integrate-ruby-rails-api.md deleted file mode 100644 index 408df5a8e2..0000000000 --- a/site/docs/v1/tech/tutorials/integrate-ruby-rails-api.md +++ /dev/null @@ -1,199 +0,0 @@ ---- -layout: doc -title: Integrate Your Ruby on Rails API With FusionAuth -description: Integrate your Ruby on Rails API with FusionAuth -navcategory: getting-started -prerequisites: Ruby, bundler and Rails -technology: Ruby on Rails -language: Ruby ---- - -## Integrate Your {{page.technology}} API With FusionAuth - -{% include docs/integration/_intro-api.md %} - -## Prerequisites - -{% include docs/integration/_prerequisites.md %} - -## Download and Install FusionAuth - -{% include docs/integration/_install-fusionauth.md %} - -## Create a User and an API Key - -{% include docs/integration/_add-user.md %} - -## Configure FusionAuth - -Next, you need to set up FusionAuth. This can be done in different ways, but we’re going to use the [{{page.language}} client library](/docs/v1/tech/client-libraries/ruby). You can use the client library with an IDE of your preference as well. - -First, make a directory: - -```shell -mkdir setup-fusionauth && cd setup-fusionauth -``` - -Then, create the required files: - -```shell -touch Gemfile -``` - -Now, copy and paste the following file into `Gemfile`. - -```ruby -{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-client-libraries/main/ruby/Gemfile %} -``` - -Install the gems. - -```shell -bundle install -``` - -Create a file called `setup.rb`. Then copy and paste the following code into it. - -```ruby -{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-client-libraries/main/ruby/setup-api.rb %} -``` - -Then, you can run the setup script. - -{% include _callout-note.liquid content="The setup script is designed to run on a newly installed FusionAuth instance with only one user and no tenants other than `Default`. To follow this guide on a FusionAuth instance that does not meet these criteria, you may need to modify the above script.

    Refer to the [Ruby client library](/docs/v1/tech/client-libraries/ruby) documentation for more information." %} - -This will create the FusionAuth configuration for your {{page.technology}} API. - -```shell -fusionauth_api_key=YOUR_API_KEY_FROM_ABOVE ruby setup.rb -``` - -If you are using PowerShell, you will need to set the environment variable in a separate command before executing the script. - -```shell -$env:fusionauth_api_key='YOUR_API_KEY_FROM_ABOVE' -ruby setup.rb -``` - -If you want, you can [log into your instance](http://localhost:9011) and examine the new API configuration the script created for you. You'd navigate to the Applications tab to do so. - -## Create Your {{page.technology}} API - -Now you are going to create a {{page.technology}} API. While this section builds a simple {{page.technology}} API, you can use the same configuration to build a more complex {{page.technology}} API. - -First, create the skeleton of the {{page.technology}} API. Rails has a nice generator to build this out. - -```shell -rails new myapi --api && cd myapi -``` - -Now, update your `Gemfile` to look like this: - -```text -{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-rails-api-guide/main/Gemfile %} -``` - -You may need to modify the version of ruby specified in the Gemfile. As long as Rails 7 is supported, you will be fine. - -Then, install these new gems. - -```shell -bundle install -``` - -Next, create a file called `.env.development` and insert the following into it. - -```ini -{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-rails-api-guide/main/.env.development %} -``` - -You can now start writing the code for your Rails API. First, let's create a controller which gives back a JSON message. Create a new file called `app/controllers/messages_controller.rb`, then add the following code: - -```ruby -{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-rails-api-guide/main/app/controllers/messages_controller.rb %} -``` - -This controller returns a JSON array with messages. - -Next, update the `config/routes.rb` file to look like this: - -```ruby -{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-rails-api-guide/main/config/routes.rb %} -``` - -This tells {{page.technology}} to return the content generated by the `messages_controller.rb` file when the `/messages` path is set up. - -You can now start up your server. You should do it in a new terminal window so that you can continue to edit the {{page.technology}} code. - -```shell -bundle e rails s -p 4001 -``` - -And visit [http://localhost:4001/messages](http://localhost:4001/messages) and view the JSON. - -Next, let's configure the token protection for this API. Go back to your previous terminal window and create a `config/initializers/jwt_rack.rb` file, and update it to look like this: - -```ruby -{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-rails-api-guide/main/config/initializers/jwt_rack.rb %} -``` - -This tells {{page.technology}} to check for various attributes of the token, including the `iss` and the `aud`. Read the [`rack_jwt` gem documentation](https://github.com/FusionAuth/rack-jwt/) for more. - -You can also access the JWT in the controller. Below, the messages controller adds a special message if the user has a certain role. - -```ruby -{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-example-rails-api-guide/main/app/controllers/messages_controller.rb %} -``` - -Now, back to the terminal where your server is running. Stop it (using `control-C`) and restart it. - -```shell -bundle e rails s -p 4001 -``` - -Visit [http://localhost:4001/messages](http://localhost:4001/messages), you'll get an error: - -```json -{"error":"Missing token cookie and Authorization header"} -``` - -Your API is protected. Now, let's get an access token so authorized clients can get the API results. - -## Testing the API Flow - -There are a number of ways to get an access token, as mentioned, but for clarity, let's use the login API to mimic a client. - -Run this command in a terminal: - -```shell -curl -H 'Authorization: YOUR_API_KEY_FROM_ABOVE' \ - -H 'Content-type: application/json' \ - -d '{"loginId": "YOUR_EMAIL", "password":"YOUR_PASSWORD","applicationId": "e9fdb985-9173-4e01-9d73-ac2d60d1dc8e"}' \ - http://localhost:9011/api/login -``` - -Replace `YOUR_EMAIL` and `YOUR_PASSWORD` with the username and password you set up previously. - -This request will return something like this: - -```json -{% remote_include https://raw.githubusercontent.com/FusionAuth/fusionauth-site/master/site/docs/src/json/users/login-response.json %} -``` - -Grab the `token` field (which begins with `ey`). Replace YOUR_TOKEN below with that value, and run this command: - -```shell -curl --cookie 'app.at=YOUR_TOKEN' http://localhost:4001/messages -``` - -Here you are placing the token in a cookie named `app.at`. This is for compatibility with the FusionAuth best practices and [the hosted backend](/docs/v1/tech/apis/hosted-backend). - -If you want to store it in a different cookie or send it in the header, make sure you modify the `rack_jwt` initializer and restart the {{page.technology}} API. - -This will result in the JSON below. - -```json -{"messages":["Hello"]} -``` - -