├── .github └── workflows │ └── super-linter.yml ├── README.md ├── ruby-admin-portal-example ├── .env.example ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── README.md ├── app.rb ├── public │ ├── images │ │ ├── settings-2.svg │ │ ├── workos-logo-with-text.png │ │ └── workos_logo_new.png │ └── stylesheets │ │ └── styles.css └── views │ ├── admin_portal_launcher.erb │ ├── index.erb │ └── layout.erb ├── ruby-audit-logs-example ├── .env.example ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── app.rb ├── public │ ├── images │ │ ├── blurple_wombat.png │ │ ├── clipboard-edit.svg │ │ ├── download.svg │ │ ├── eye.svg │ │ ├── send.svg │ │ ├── settings-2.svg │ │ ├── settings.svg │ │ ├── share-2.svg │ │ ├── workos-logo-with-text.png │ │ ├── workos_favicon.png │ │ └── workos_logo_new.png │ └── login.css └── views │ ├── layout.erb │ ├── login.erb │ └── send_events.erb ├── ruby-directory-sync-example ├── .env.example ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── app.rb ├── public │ ├── images │ │ ├── settings-2.svg │ │ ├── workos-logo-with-text.png │ │ ├── workos_favicon.png │ │ └── workos_logo_new.png │ └── styles │ │ └── styles.css └── views │ ├── directory.erb │ ├── group.erb │ ├── index.erb │ ├── layout.erb │ ├── user.erb │ └── webhooks.erb ├── ruby-magic-link-example ├── .env.example ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── app.rb ├── public │ ├── images │ │ ├── workos_full_logo.png │ │ └── workos_logo_new.png │ ├── styles.css │ └── workos_logo_new.png └── views │ ├── check_email.erb │ ├── index.erb │ └── layout.erb ├── ruby-mfa-example ├── .env.example ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── app.rb ├── public │ ├── images │ │ ├── settings.svg │ │ ├── workos_full_logo.png │ │ └── workos_logo_new.png │ └── styles │ │ └── styles.css └── views │ ├── challenge_factor.erb │ ├── challenge_success.erb │ ├── enroll_factor.erb │ ├── factor_detail.erb │ ├── index.erb │ └── layout.erb ├── ruby-rails-sso-example ├── .browserslistrc ├── .env.example ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── Procfile.dev ├── README.md ├── Rakefile ├── app │ ├── assets │ │ ├── config │ │ │ └── manifest.js │ │ ├── images │ │ │ ├── .keep │ │ │ ├── google-button.png │ │ │ ├── microsoft-button.png │ │ │ ├── saml-button.png │ │ │ ├── workos-logo-with-text.png │ │ │ └── workos_logo_new.png │ │ └── stylesheets │ │ │ └── application.css │ ├── channels │ │ └── application_cable │ │ │ ├── channel.rb │ │ │ └── connection.rb │ ├── controllers │ │ ├── application_controller.rb │ │ ├── concerns │ │ │ └── .keep │ │ └── users │ │ │ ├── registrations_controller.rb │ │ │ └── sessions_controller.rb │ ├── helpers │ │ └── application_helper.rb │ ├── javascript │ │ ├── channels │ │ │ ├── consumer.js │ │ │ └── index.js │ │ ├── packs │ │ │ └── application.js │ │ └── stylesheets │ │ │ └── application.scss │ ├── jobs │ │ └── application_job.rb │ ├── mailers │ │ └── application_mailer.rb │ ├── models │ │ ├── application_record.rb │ │ ├── concerns │ │ │ └── .keep │ │ └── user.rb │ └── views │ │ ├── application │ │ └── home.html.erb │ │ ├── layouts │ │ ├── application.html.erb │ │ ├── mailer.html.erb │ │ └── mailer.text.erb │ │ └── users │ │ ├── registrations │ │ ├── edit.html.erb │ │ └── new.html.erb │ │ ├── sessions │ │ └── new.html.erb │ │ └── shared │ │ ├── _error_messages.html.erb │ │ └── _links.html.erb ├── babel.config.js ├── bin │ ├── bundle │ ├── rails │ ├── rake │ ├── setup │ ├── spring │ ├── webpack │ ├── webpack-dev-server │ └── yarn ├── config.ru ├── config │ ├── application.rb │ ├── boot.rb │ ├── cable.yml │ ├── credentials.yml.enc │ ├── database.yml │ ├── environment.rb │ ├── environments │ │ ├── development.rb │ │ ├── production.rb │ │ └── test.rb │ ├── initializers │ │ ├── application_controller_renderer.rb │ │ ├── assets.rb │ │ ├── backtrace_silencers.rb │ │ ├── content_security_policy.rb │ │ ├── cookies_serializer.rb │ │ ├── devise.rb │ │ ├── filter_parameter_logging.rb │ │ ├── inflections.rb │ │ ├── mime_types.rb │ │ ├── new_framework_defaults_7_0.rb │ │ ├── permissions_policy.rb │ │ └── wrap_parameters.rb │ ├── locales │ │ ├── devise.en.yml │ │ └── en.yml │ ├── puma.rb │ ├── routes.rb │ ├── spring.rb │ ├── storage.yml │ ├── webpack │ │ ├── development.js │ │ ├── environment.js │ │ ├── production.js │ │ └── test.js │ └── webpacker.yml ├── db │ ├── migrate │ │ ├── 20201117212002_devise_create_users.rb │ │ ├── 20201117230916_add_columns_to_users.rb │ │ ├── 20220322000320_create_active_storage_tables.active_storage.rb │ │ ├── 20220322233528_add_service_name_to_active_storage_blobs.active_storage.rb │ │ ├── 20220322233529_create_active_storage_variant_records.active_storage.rb │ │ └── 20220322233530_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb │ ├── schema.rb │ └── seeds.rb ├── lib │ ├── assets │ │ └── .keep │ └── tasks │ │ └── .keep ├── log │ └── .keep ├── package.json ├── postcss.config.js ├── public │ ├── 404.html │ ├── 422.html │ ├── 500.html │ ├── apple-touch-icon-precomposed.png │ ├── apple-touch-icon.png │ ├── favicon.ico │ └── robots.txt ├── storage │ └── .keep ├── test │ ├── application_system_test_case.rb │ ├── channels │ │ └── application_cable │ │ │ └── connection_test.rb │ ├── controllers │ │ └── .keep │ ├── fixtures │ │ ├── .keep │ │ ├── files │ │ │ └── .keep │ │ └── users.yml │ ├── helpers │ │ └── .keep │ ├── integration │ │ └── .keep │ ├── mailers │ │ └── .keep │ ├── models │ │ ├── .keep │ │ └── user_test.rb │ ├── system │ │ └── .keep │ └── test_helper.rb ├── tmp │ ├── .keep │ └── pids │ │ └── .keep ├── vendor │ └── .keep └── yarn.lock └── ruby-sso-example ├── .DS_Store ├── .env.example ├── .gitignore ├── .ruby-version ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── app.rb ├── public ├── images │ ├── google-button.png │ ├── microsoft-button.png │ ├── saml-button.png │ ├── workos-logo-with-text.png │ └── workos_logo_new.png └── styles.css └── views ├── index.erb └── layout.erb /.github/workflows/super-linter.yml: -------------------------------------------------------------------------------- 1 | # This workflow executes several linters on changed files based on languages used in your code base whenever 2 | # you push a code or open a pull request. 3 | # 4 | # You can adjust the behavior by modifying this file. 5 | # For more information, see: 6 | # https://github.com/github/super-linter 7 | # name: Lint Code Base 8 | 9 | # on: 10 | # push: 11 | # branches: [ "main" ] 12 | # pull_request: 13 | # branches: [ "main" ] 14 | # jobs: 15 | # run-lint: 16 | # runs-on: ubuntu-latest 17 | # steps: 18 | # - name: Checkout code 19 | # uses: actions/checkout@v3 20 | # with: 21 | # # Full git history is needed to get a proper list of changed files within `super-linter` 22 | # fetch-depth: 0 23 | 24 | # - name: Lint Code Base 25 | # uses: github/super-linter@v4 26 | # env: 27 | # VALIDATE_ALL_CODEBASE: false 28 | # DEFAULT_BRANCH: "main" 29 | # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby and Rails Example Applications powered by WorkOS 2 | 3 | Example applications demonstrating to use the [WorkOS Ruby SDK](https://github.com/workos/workos-ruby) for SSO, Directory Sync, Admin Portal and Magic Link. 4 | 5 | ## For more information, please see the following guides: 6 | 7 | * [Single Sign-On](https://workos.com/docs/sso/guide) 8 | * [Directory Sync](https://workos.com/docs/directory-sync/guide) 9 | * [Admin Portal](https://workos.com/docs/admin-portal/guide) 10 | * [Magic Link](https://workos.com/docs/magic-link/guide) 11 | * [API Reference](https://workos.com/docs/reference) 12 | 13 | ## Need help? 14 | 15 | If you get stuck and aren't able to resolve the issue by reading our [WorkOS Ruby SDK documentation](https://docs.workos.com/sdk/ruby) or [API reference](https://workos.com/docs/reference), you can reach out to us at support@workos.com and we'll lend a hand! 16 | -------------------------------------------------------------------------------- /ruby-admin-portal-example/.env.example: -------------------------------------------------------------------------------- 1 | WORKOS_API_KEY= 2 | WORKOS_CLIENT_ID= -------------------------------------------------------------------------------- /ruby-admin-portal-example/.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /ruby-admin-portal-example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 -------------------------------------------------------------------------------- /ruby-admin-portal-example/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'dotenv', '2.7.6' 6 | gem 'sinatra', '2.2.0' 7 | gem 'workos', '2.13.0' 8 | -------------------------------------------------------------------------------- /ruby-admin-portal-example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | dotenv (2.7.6) 5 | mustermann (1.1.1) 6 | ruby2_keywords (~> 0.0.1) 7 | rack (2.2.3.1) 8 | rack-protection (2.2.0) 9 | rack 10 | ruby2_keywords (0.0.5) 11 | sinatra (2.2.0) 12 | mustermann (~> 1.0) 13 | rack (~> 2.2) 14 | rack-protection (= 2.2.0) 15 | tilt (~> 2.0) 16 | sorbet-runtime (0.5.10830) 17 | tilt (2.0.10) 18 | workos (2.13.0) 19 | sorbet-runtime (~> 0.5) 20 | 21 | PLATFORMS 22 | ruby 23 | 24 | DEPENDENCIES 25 | dotenv (= 2.7.6) 26 | sinatra (= 2.2.0) 27 | workos (= 2.13.0) 28 | 29 | BUNDLED WITH 30 | 2.1.4 31 | -------------------------------------------------------------------------------- /ruby-admin-portal-example/README.md: -------------------------------------------------------------------------------- 1 | # Ruby Example App with Admin Portal powered by WorkOS 2 | 3 | An example application demonstrating to use the [WorkOS Ruby SDK](https://github.com/workos/workos-ruby) to access the Admin Portal for SSO and Directory Sync. 4 | 5 | ## Prerequisites 6 | 7 | Ruby 2.7.2 8 | 9 | ## Ruby Project Setup 10 | 11 | 1. Clone the main repo and install dependencies for the app you'd like to use: 12 | ```bash 13 | # HTTPS 14 | git clone https://github.com/workos/ruby-example-applications.git 15 | ``` 16 | or 17 | 18 | ```bash 19 | # SSH 20 | git clone git@github.com:workos/ruby-example-applications.git 21 | ``` 22 | 23 | 2. Navigate to the Admin Portal app within the cloned repo. 24 | ```bash 25 | $ cd ruby-example-applications/ruby-admin-portal-example 26 | ``` 27 | 28 | 3. Install the dependencies. 29 | ```bash 30 | $ bundle install 31 | ``` 32 | ## Configure your environment 33 | 34 | 1. Grab your [API Key](https://dashboard.workos.com/api-keys). 35 | 2. Get your [Client ID](https://dashboard.workos.com/configuration). 36 | 3. Create a `.env` file at the root of the project and populate with the 37 | following environment variables (using values found above): 38 | 39 | ```typescript 40 | WORKOS_API_KEY=your_api_key_here 41 | WORKOS_CLIENT_ID=your_client_id_here 42 | ``` 43 | 44 | 4. Set your [Default Redirect Link](https://dashboard.workos.com/configuration). 45 | 46 | ## Run the server and log in using SSO 47 | 48 | ```sh 49 | ruby app.rb 50 | ``` 51 | 52 | Head to `http://localhost:4567/` to begin! 53 | 54 | 55 | ## Need help? 56 | 57 | If you get stuck and aren't able to resolve the issue by reading our [WorkOS Admin Portal documentation](https://workos.com/docs/admin-portal/guide/introduction), API reference, or tutorials, you can reach out to us at support@workos.com and we'll lend a hand. -------------------------------------------------------------------------------- /ruby-admin-portal-example/app.rb: -------------------------------------------------------------------------------- 1 | require 'dotenv/load' 2 | require 'sinatra' 3 | require 'workos' 4 | require 'json' 5 | 6 | $organization 7 | 8 | get '/' do 9 | 10 | erb :index 11 | end 12 | 13 | post '/provision-enterprise' do 14 | domains = params['domain'].split(" ") 15 | organizationName = params['org'] 16 | # if an organization does exist with the domain, use that organization for connection 17 | organizations = WorkOS::Organizations.list_organizations( 18 | domains: domains 19 | ) 20 | if organizations.data.length == 0 21 | $organization = WorkOS::Organizations.create_organization( 22 | name: organizationName, 23 | domains: domains 24 | ) 25 | else 26 | $organization = organizations.data[0] 27 | end 28 | erb :admin_portal_launcher 29 | end 30 | 31 | 32 | get('/launch-admin-portal') do 33 | intent = params["intent"] 34 | organization_id = $organization.id # ... The ID of the organization to start an Admin Portal session for 35 | portal_link = WorkOS::Portal.generate_link( 36 | organization: organization_id, 37 | intent: intent, 38 | ) 39 | redirect portal_link 40 | end -------------------------------------------------------------------------------- /ruby-admin-portal-example/public/images/settings-2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-admin-portal-example/public/images/workos-logo-with-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-admin-portal-example/public/images/workos-logo-with-text.png -------------------------------------------------------------------------------- /ruby-admin-portal-example/public/images/workos_logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-admin-portal-example/public/images/workos_logo_new.png -------------------------------------------------------------------------------- /ruby-admin-portal-example/views/admin_portal_launcher.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

Which Admin Portal would you like to launch?

5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 34 | 35 |
IntentLaunch Session
SSO 14 | " class="icon" alt="workos logo"> 15 |
Directory Sync 20 | " class="icon" alt="workos logo"> 21 |
Audit Logs 26 | " class="icon" alt="workos logo"> 27 |
Log Streams 32 | " class="icon" alt="workos logo"> 33 |
36 |
37 |
38 |
-------------------------------------------------------------------------------- /ruby-admin-portal-example/views/index.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 | Admin Portal Example 8 |
9 |
10 |
11 | 19 |
20 |
21 | 29 |
30 |
31 |
32 | 35 |
36 |
37 |
38 |
39 |
40 |
-------------------------------------------------------------------------------- /ruby-admin-portal-example/views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | > 5 | 6 | 7 | 8 | 9 |
10 |
11 |
12 | " alt="workos logo"> 13 |
14 |
15 |
16 | 17 | 19 | 21 | 22 |
23 |
24 |
25 |
26 | <%= yield %> 27 |
28 |
29 | 30 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/.env.example: -------------------------------------------------------------------------------- 1 | WORKOS_API_KEY='' 2 | WORKOS_CLIENT_ID='' 3 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 -------------------------------------------------------------------------------- /ruby-audit-logs-example/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'dotenv', '2.7.6' 6 | gem 'sinatra', '2.2.0' 7 | gem 'workos', '2.12.0' 8 | 9 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | coderay (1.1.3) 5 | dotenv (2.7.6) 6 | method_source (1.0.0) 7 | mustermann (1.1.2) 8 | ruby2_keywords (~> 0.0.1) 9 | pry (0.14.2) 10 | coderay (~> 1.1) 11 | method_source (~> 1.0) 12 | rack (2.2.4) 13 | rack-protection (2.2.0) 14 | rack 15 | ruby2_keywords (0.0.5) 16 | sinatra (2.2.0) 17 | mustermann (~> 1.0) 18 | rack (~> 2.2) 19 | rack-protection (= 2.2.0) 20 | tilt (~> 2.0) 21 | sorbet-runtime (0.5.10667) 22 | tilt (2.0.11) 23 | workos (2.9.0) 24 | sorbet-runtime (~> 0.5) 25 | 26 | PLATFORMS 27 | ruby 28 | 29 | DEPENDENCIES 30 | dotenv (= 2.7.6) 31 | pry 32 | sinatra (= 2.2.0) 33 | workos (= 2.9.0) 34 | 35 | BUNDLED WITH 36 | 2.3.19 37 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 WorkOS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/README.md: -------------------------------------------------------------------------------- 1 | # ruby-audit-logs-example 2 | 3 | An example Ruby application demonstrating how to use the [WorkOS Ruby SDK](https://github.com/workos/workos-ruby) to send and retrieve Audit Log events. This example is not meant to show a real-world example of an Audit Logs implementation, but rather to show concrete examples of how events can be sent using the Ruby SDK. 4 | 5 | ## Clone and Install 6 | 7 | 1. Clone the main repo: 8 | 9 | ```sh 10 | # HTTPS 11 | $ git clone https://github.com/workos/ruby-example-applications.git 12 | ``` 13 | 14 | 2. Navigate to the Audit Logs example app within the cloned repo and install dependencies: 15 | 16 | ```sh 17 | $ cd ruby-example-applications/ruby-audit-logs-example && bundle install 18 | ``` 19 | 20 | ## Configure your environment 21 | 22 | 1. Grab your [API Key](https://dashboard.workos.com/api-keys) and your [Client ID](https://dashboard.workos.com/configuration). 23 | 2. Run `cp .env.example .env` and add your API key and Client ID. The `workos` gem will read your API key from the ENV variable `WORKOS_API_KEY` and your Client ID from the ENV variable `WORKOS_CLIENT_ID`. You may also set the API key and Client ID yourself by adding `WorkOS.key = $YOUR_API_KEY` and `CLIENT_ID = $YOUR_CLIENT_ID` to `app.rb`. 24 | 25 | ### Audit Logs Setup with WorkOS 26 | 27 | 1. Follow the [Audit Logs configuration steps](https://workos.com/docs/audit-logs/emit-an-audit-log-event/sign-in-to-your-workos-dashboard-account-and-configure-audit-log-event-schemas) to set up the following 5 events that are sent with this example: 28 | 29 | Action title: "user.signed_in" | Target type: "team" 30 | Action title: "user.logged_out" | Target type: "team" 31 | Action title: "user.organization_set" | Target type: "team" 32 | Action title: "user.organization_deleted" | Target type: "team" 33 | Action title: "user.connection_deleted" | Target type: "team" 34 | 35 | 2. Next, take note of the Organization ID for the Org which you will be sending the Audit Log events for. This ID gets entered into the splash page of the example application. 36 | 37 | 3. Once you enter the Organization ID and submit it, you will be brought to the page where you'll be able to send the audit log events that were just configured. You'll also notice that the action of setting the Organization triggered an Audit Log already. Click the buttons to send the respective events. 38 | 39 | 4. To obtain a CSV of the Audit Log events that were sent for the last 30 days, click the "Export Events" button. This will bring you to a new page where you can download the events. Downloading the events is a 2 step process. First you need to create the report by clicking the "Generate CSV" button. Then click the "Access CSV" button to download a CSV of the Audit Log events for the selected Organization for the past 30 days. 40 | 41 | ## Run the app 42 | 43 | ```sh 44 | ruby app.rb 45 | ``` 46 | 47 | ## Audit Logs Setup with WorkOS 48 | 49 | 5. Follow the [Audit Logs configuration steps](https://workos.com/docs/audit-logs/emit-an-audit-log-event/sign-in-to-your-workos-dashboard-account-and-configure-audit-log-event-schemas) to set up the following 2 events that are sent with this example: 50 | 51 | Action title: "user.organization_set" | Target type: "team" 52 | Action title: "user.organization_deleted" | Target type: "team" 53 | 54 | 6. Configure the Admin Portal Redirect URI. 55 | 56 | Navigate to the Configuration tab in your WorkOS Dshboard. From there click the Admin Portal tab. Click the Edit Admin Portal Redirect Links button and add "http://localhost:4567" to the "When clicking the back navigation, return users to:" input, then click Save Redirect Links. 57 | 58 | 7. To obtain a CSV of the Audit Log events that were sent for the last 30 days, click the "Export Events" tab. This will bring you to a new page where you can download the events. Downloading the events is a 2 step process. First you need to create the report by clicking the "Generate CSV" button. Then click the "Access CSV" button to download a CSV of the Audit Log events for the selected Organization for the past 30 days. You may also adjust the time range using the form inputs. 59 | 60 | ## Need help? 61 | 62 | If you get stuck and aren't able to resolve the issue by reading our API reference or tutorials, you can reach out to us at support@workos.com and we'll lend a hand. 63 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/app.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'dotenv/load' 4 | require 'sinatra' 5 | require 'workos' 6 | require 'date' 7 | 8 | # Pull API key and Client ID from ENV variable 9 | WorkOS.key = ENV['WORKOS_API_KEY'] 10 | CLIENT_ID = ENV['WORKOS_CLIENT_ID'] 11 | 12 | enable :sessions 13 | 14 | use( 15 | Rack::Session::Cookie, 16 | key: 'rack.session', 17 | domain: 'localhost', 18 | path: '/', 19 | expire_after: 2_592_000, 20 | secret: SecureRandom.hex(16) 21 | ) 22 | 23 | get '/' do 24 | before = params[:before] 25 | after = params[:after] 26 | if !before 27 | @organizations = WorkOS::Organizations.list_organizations( 28 | limit: 5 29 | ) 30 | else 31 | @organizations = WorkOS::Organizations.list_organizations( 32 | limit: 5, 33 | before: before, 34 | after: after 35 | ) 36 | end 37 | @before = @organizations.list_metadata["before"] 38 | @after = @organizations.list_metadata["after"] 39 | erb :login, :layout => :layout 40 | end 41 | 42 | 43 | get '/set_org' do 44 | @organization = WorkOS::Organizations.get_organization( 45 | id: params[:id] 46 | ) 47 | @today_iso = Time.now.utc.iso8601 48 | @last_month_iso = (Time.now - (30 * 86400)).utc.iso8601 49 | erb :send_events, :layout => :layout 50 | end 51 | 52 | get '/events' do 53 | link = WorkOS::Portal.generate_link( 54 | organization: params[:organization_id], 55 | intent: params[:intent], 56 | ) 57 | redirect link 58 | end 59 | 60 | post '/send_events' do 61 | 62 | organization_id = params["organization_id"] 63 | 64 | event = { 65 | "action": "user.organization_deleted", 66 | "version": params[:event_version].to_i, 67 | "occurred_at": Time.now.utc.iso8601, 68 | "actor": { 69 | "type": params[:actor_type], 70 | "name": params[:actor_name], 71 | "id": "user_01GBNJC3MX9ZZJW1FSTF4C5938", 72 | }, 73 | "targets": [ 74 | { 75 | "type": params[:target_type], 76 | "name": params[:target_name], 77 | "id": "team_01GBNJD4MKHVKJGEWK42JNMBGS", 78 | }, 79 | ], 80 | "context": { 81 | "location": "123.123.123.123", 82 | "user_agent": "Chrome/104.0.0.0", 83 | }, 84 | } 85 | WorkOS::AuditLogs.create_event( 86 | organization: organization_id, 87 | event: event, 88 | ) 89 | 90 | redirect to("/set_org?id=#{organization_id}") 91 | 92 | 93 | end 94 | 95 | 96 | post '/get_events' do 97 | 98 | organization_id = params[:organization_id] 99 | 100 | event_type = params[:event] 101 | today = DateTime.now.to_s 102 | last_month = DateTime.now.prev_month.to_s 103 | if params[:filter_actions] != "" 104 | actions = params[:filter_actions] 105 | else 106 | actions = nil 107 | end 108 | 109 | if params[:filter_actors] != "" 110 | actors = params[:filter_actors] 111 | else 112 | actors = nil 113 | end 114 | 115 | if params[:filter_targets] != "" 116 | targets = params[:filter_targets] 117 | else 118 | targets = nil 119 | end 120 | 121 | if event_type == 'generate_csv' 122 | audit_log_export = WorkOS::AuditLogs.create_export( 123 | organization: organization_id, 124 | range_start: params[:range_start], 125 | range_end: params[:range_end], 126 | actions: actions, 127 | actors: actors, 128 | targets: targets 129 | ) 130 | session[:export_id] = audit_log_export.id 131 | redirect to("/set_org?id=#{organization_id}") 132 | end 133 | 134 | if event_type == 'access_csv' 135 | export_id = session[:export_id].to_s 136 | audit_log_export = WorkOS::AuditLogs.get_export( 137 | id: export_id 138 | ) 139 | url = audit_log_export.url 140 | 141 | redirect url 142 | end 143 | 144 | end 145 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/blurple_wombat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-audit-logs-example/public/images/blurple_wombat.png -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/clipboard-edit.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/download.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/eye.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/send.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/settings-2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/share-2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/workos-logo-with-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-audit-logs-example/public/images/workos-logo-with-text.png -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/workos_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-audit-logs-example/public/images/workos_favicon.png -------------------------------------------------------------------------------- /ruby-audit-logs-example/public/images/workos_logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-audit-logs-example/public/images/workos_logo_new.png -------------------------------------------------------------------------------- /ruby-audit-logs-example/views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WorkOS Ruby Audit Logs Example 5 | "> 6 | 7 | 8 | 9 | 10 |
11 | <%= yield %> 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /ruby-audit-logs-example/views/login.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |

Audit Logs

7 | " class="icon" alt="settings icon"> 8 |
9 |

Sinatra Example App

10 |
11 |
12 |
13 |
14 | 15 | 21 | 22 | 28 |
29 |
30 |
31 |
32 |
33 |
34 | 35 |
36 |
37 | 39 |
40 |
41 | 43 |
44 | 49 |
50 |
51 |
52 |
53 |

Select Organization

54 |
55 |
56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | <% @organizations.data.each do |i| %> 64 | 65 | 66 | 67 | 68 | 69 | <% end %> 70 |
OrganizationIDView Settings
<%= i.name %><%= i.id %>" class="icon" alt="workos logo">
71 |
72 |
73 | <% if @after %> 74 |
75 | 76 |
77 | <% end %> 78 | <% if @before %> 79 |
80 | 81 |
82 | <% end %> 83 |
84 |
85 |
86 |
87 | 96 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/.env.example: -------------------------------------------------------------------------------- 1 | WORKOS_API_KEY= 2 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 2 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # frozen_string_literal: true 3 | 4 | source 'https://rubygems.org' 5 | 6 | gem 'dotenv', '2.7.6' 7 | gem 'sinatra', '2.2.0' 8 | gem 'workos', '2.13.0' 9 | gem 'sinatra-websocket', '0.3.1' 10 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.8.1) 5 | public_suffix (>= 2.0.2, < 6.0) 6 | daemons (1.4.1) 7 | dotenv (2.7.6) 8 | em-websocket (0.3.8) 9 | addressable (>= 2.1.1) 10 | eventmachine (>= 0.12.9) 11 | eventmachine (1.2.7) 12 | mustermann (1.1.2) 13 | ruby2_keywords (~> 0.0.1) 14 | public_suffix (5.0.1) 15 | rack (2.2.6.4) 16 | rack-protection (2.2.0) 17 | rack 18 | ruby2_keywords (0.0.5) 19 | sinatra (2.2.0) 20 | mustermann (~> 1.0) 21 | rack (~> 2.2) 22 | rack-protection (= 2.2.0) 23 | tilt (~> 2.0) 24 | sinatra-websocket (0.3.1) 25 | em-websocket (~> 0.3.6) 26 | eventmachine 27 | thin (>= 1.3.1, < 2.0.0) 28 | sorbet-runtime (0.5.10827) 29 | thin (1.8.1) 30 | daemons (~> 1.0, >= 1.0.9) 31 | eventmachine (~> 1.0, >= 1.0.4) 32 | rack (>= 1, < 3) 33 | tilt (2.1.0) 34 | workos (2.13.0) 35 | sorbet-runtime (~> 0.5) 36 | 37 | PLATFORMS 38 | ruby 39 | 40 | DEPENDENCIES 41 | dotenv (= 2.7.6) 42 | sinatra (= 2.2.0) 43 | sinatra-websocket (= 0.3.1) 44 | workos (= 2.13.0) 45 | 46 | BUNDLED WITH 47 | 2.1.4 48 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 WorkOS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/README.md: -------------------------------------------------------------------------------- 1 | # ruby-directory-sync-example 2 | 3 | An example Sinatra application demonstrating how Directory Sync works with WorkOS and Ruby. 4 | 5 | ## Clone and Install 6 | 7 | 1. Clone the main repo: 8 | 9 | ```sh 10 | git clone https://github.com/workos/ruby-example-applications.git 11 | ``` 12 | 13 | 2. Navigate to the Ruby Directory Sync app within the main repo and install dependencies: 14 | 15 | ```sh 16 | cd ruby-example-applications/ruby-directory-sync-example && bundle install 17 | ``` 18 | 19 | ## Configure your environment 20 | 21 | 1. Grab your [API Key](https://dashboard.workos.com/api-keys). 22 | 2. Run `cp .env.example .env` and add your API key. The `workos` gem will read your API key from the ENV variable `WORKOS_API_KEY`. You may also set the API key yourself by adding `WorkOS.key = $YOUR_API_KEY` to `app.rb`. 23 | 24 | ## Run the app 25 | 26 | ```sh 27 | ruby app.rb 28 | ``` 29 | 30 | Head to `http://localhost:4567`! 31 | 32 | ## Testing Webhooks 33 | 34 | ### 1. Click on the "Test Webhooks" button to navigate to the webhooks view. 35 | 36 | 37 | ### 2. Start an `ngrok` session 38 | 39 | [Ngrok](https://ngrok.com/) is a simple application that allows you to map a local endpoint to a public endpoint. 40 | 41 | The application will run on http://localhost:4567. Ngrok will create a tunnel to the application so we can receive webhooks from WorkOS. 42 | 43 | ```sh 44 | ./ngrok http 4567 45 | ``` 46 | 47 | ### 3. Set Up a WorkOS Endpoint 48 | 49 | Log into the [WorkOS Dashboard](https://dashboard.workos.com/webhooks) and add a Webhook endpoint with the public ngrok URL with `/webhooks` appended. 50 | 51 | The local application is listening for webhook requests at http://localhost:4567/webhooks 52 | 53 | ### 4. Set Up Webhooks Secret 54 | 55 | In order for the SDK to validate that WorkOS webhooks, locate the Webhook secret from the dashboard. 56 | 57 | Then populate the following environment variable in your `.env` file at the root of the project. 58 | 59 | ```sh 60 | WORKOS_WEBHOOK_SECRET=your_webhook_secret 61 | ``` 62 | 63 | For more information, see the [WorkOS Ruby SDK documentation](https://docs.workos.com/sdk/ruby). 64 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/app.rb: -------------------------------------------------------------------------------- 1 | 2 | # frozen_string_literal: true 3 | 4 | require 'dotenv/load' 5 | require 'sinatra' 6 | require 'workos' 7 | require 'json' 8 | require 'sinatra-websocket' 9 | 10 | set :server, 'thin' 11 | set :sockets, [] 12 | 13 | # Pull API key from ENV variable 14 | WorkOS.key = ENV['WORKOS_API_KEY'] 15 | 16 | enable :sessions 17 | 18 | use( 19 | Rack::Session::Cookie, 20 | key: 'rack.session', 21 | domain: 'localhost', 22 | path: '/', 23 | expire_after: 2_592_000, 24 | secret: SecureRandom.hex(16) 25 | ) 26 | 27 | get '/' do 28 | before = params[:before] 29 | after = params[:after] 30 | puts before 31 | puts after 32 | if !before 33 | @directories = WorkOS::DirectorySync.list_directories( 34 | limit: 5 35 | ) 36 | else 37 | @directories = WorkOS::DirectorySync.list_directories( 38 | limit: 5, 39 | before: before, 40 | after: after 41 | ) 42 | end 43 | @before = @directories.list_metadata["before"] 44 | @after = @directories.list_metadata["after"] 45 | erb :index, :layout => :layout 46 | end 47 | 48 | 49 | get '/directories/:id' do 50 | @groups_list = WorkOS::DirectorySync.list_groups(directory: params[:id], limit: 100) 51 | @groups = @groups_list.data 52 | @users_list = WorkOS::DirectorySync.list_users(directory: params[:id], limit: 100) 53 | @users = @users_list.data 54 | 55 | erb :directory 56 | end 57 | 58 | get '/users/:id' do 59 | @user = WorkOS::DirectorySync.get_user(params[:id]) 60 | @user_groups_list = WorkOS::DirectorySync.list_groups(user: params[:id], limit: 5) 61 | @user_groups = @user_groups_list.data 62 | 63 | erb :user 64 | end 65 | 66 | get '/groups/:id' do 67 | @group = WorkOS::DirectorySync.get_group(params[:id]) 68 | @group_users_list = WorkOS::DirectorySync.list_users(group: params[:id]) 69 | @group_users = @group_users_list.data 70 | 71 | erb :group 72 | end 73 | 74 | 75 | get '/webhooks' do 76 | if !request.websocket? 77 | erb :webhooks 78 | else 79 | request.websocket do |ws| 80 | ws.onopen do 81 | warn("websocket opened") 82 | settings.sockets << ws 83 | end 84 | ws.onmessage do |msg| 85 | warn("websocket onmessage") 86 | EM.next_tick { settings.sockets.each{|s| s.send(msg) } } 87 | end 88 | ws.onclose do 89 | warn("websocket closed") 90 | settings.sockets.delete(ws) 91 | end 92 | end 93 | end 94 | end 95 | 96 | post '/webhooks' do 97 | payload = JSON.parse(request.body.read).to_json 98 | sig_header = request.env['HTTP_WORKOS_SIGNATURE'] 99 | verified_webhook = WorkOS::Webhooks.construct_event( 100 | payload: payload.to_s, 101 | sig_header: sig_header, 102 | secret: ENV['WORKOS_WEBHOOK_SECRET'] 103 | ) 104 | if verified_webhook 105 | EM.next_tick { settings.sockets.each{|s| s.send(payload.to_s ) } } 106 | redirect "/webhooks" 107 | else 108 | render :json => {:status => 400, :error => "Webhook failed"} and return 109 | end 110 | end 111 | 112 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/public/images/settings-2.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/public/images/workos-logo-with-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-directory-sync-example/public/images/workos-logo-with-text.png -------------------------------------------------------------------------------- /ruby-directory-sync-example/public/images/workos_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-directory-sync-example/public/images/workos_favicon.png -------------------------------------------------------------------------------- /ruby-directory-sync-example/public/images/workos_logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-directory-sync-example/public/images/workos_logo_new.png -------------------------------------------------------------------------------- /ruby-directory-sync-example/views/directory.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | <% @users.each do |user| %> 14 | 15 | 18 | 19 | 20 | <% end %> 21 | 22 |
UsersView Details
16 | <%= user.first_name %> <%= user.last_name %> 17 | " class="icon" alt="workos logo">
23 |
24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | <% @groups.each do |group| %> 34 | 35 | 38 | 39 | 40 | <% end %> 41 | 42 |
GroupsView Details
36 | <%= group.name %> 37 | " class="icon" alt="workos logo">
43 |
44 |
45 |
46 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/views/group.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

<%= @group.name %> Group Users

5 | <% if @group_users.any?%> 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <% @group_users.each do |user| %> 17 | 18 | 19 | 20 | 21 | 22 | 23 | <% end %> 24 | 25 |
NameEmailDetails
<%= user.first_name %> <%= user.last_name %><%= user.username %>" class="icon" alt="workos logo">
26 |
27 | <%else%> 28 |

There are no users in this group.

29 | <% end %> 30 |
31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/views/index.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | > 4 | 5 | 6 | 7 | 8 | 9 |
10 |
11 |
12 |

Select a Directory

13 |
14 |
15 | Test Webhooks 16 |
17 |
18 |
19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | <% @directories.data.each do |i| %> 27 | 28 | 29 | 30 | 31 | 32 | <% end %> 33 |
DirectoryIDView Settings
<%= i.name %><%= i.id %>" class="icon" alt="workos logo">
34 |
35 |
36 | <% if @after %> 37 |
38 | 39 |
40 | <% end %> 41 | <% if @before %> 42 |
43 | 44 |
45 | <% end %> 46 |
47 |
48 |
49 | 50 | 51 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | > 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | " alt="workos logo"> 14 |
15 |
16 |
17 | 18 | 20 | 22 | 23 |
24 |
25 |
26 | <%= yield %> 27 |
28 | 29 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/views/user.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |

<%= @user.first_name %> <%= @user.last_name %>

6 |
7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | <% @user.each do |key, value| %> 17 | 18 | 19 | 20 | 21 | <% end %> 22 | 23 |
KeyValue
<%= key %><%= value %>
24 |
25 |
26 |
27 | 28 | -------------------------------------------------------------------------------- /ruby-directory-sync-example/views/webhooks.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |

Live Webhooks View

7 |
8 | Tutorial 10 | Clear 11 | 12 |
13 |
14 |
15 | 16 |
17 |
18 | 19 |
20 |
21 |
22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /ruby-magic-link-example/.env.example: -------------------------------------------------------------------------------- 1 | WORKOS_API_KEY= 2 | WORKOS_CLIENT_ID= -------------------------------------------------------------------------------- /ruby-magic-link-example/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /ruby-magic-link-example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 2 | -------------------------------------------------------------------------------- /ruby-magic-link-example/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'dotenv', '2.7.6' 6 | gem 'sinatra', '2.2.0' 7 | gem 'workos', '2.13.0' 8 | 9 | -------------------------------------------------------------------------------- /ruby-magic-link-example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | dotenv (2.7.6) 5 | mustermann (1.1.2) 6 | ruby2_keywords (~> 0.0.1) 7 | rack (2.2.6.4) 8 | rack-protection (2.2.0) 9 | rack 10 | ruby2_keywords (0.0.5) 11 | sinatra (2.2.0) 12 | mustermann (~> 1.0) 13 | rack (~> 2.2) 14 | rack-protection (= 2.2.0) 15 | tilt (~> 2.0) 16 | sorbet-runtime (0.5.10741) 17 | tilt (2.1.0) 18 | workos (2.12.0) 19 | sorbet-runtime (~> 0.5) 20 | 21 | PLATFORMS 22 | ruby 23 | 24 | DEPENDENCIES 25 | dotenv (= 2.7.6) 26 | sinatra (= 2.2.0) 27 | workos (= 2.12.0) 28 | 29 | BUNDLED WITH 30 | 2.1.4 31 | -------------------------------------------------------------------------------- /ruby-magic-link-example/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 WorkOS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ruby-magic-link-example/README.md: -------------------------------------------------------------------------------- 1 | # ruby-magic-link-example 2 | 3 | An example Sinatra application demonstrating how Magic Link works with WorkOS and Ruby. 4 | 5 | ## Clone and Install 6 | 7 | 1. Clone the main repo: 8 | 9 | ```sh 10 | git clone https://github.com/workos/ruby-example-applications.git 11 | ``` 12 | 13 | 2. Navigate to the Ruby Magic Link app within the main repo and install dependencies: 14 | 15 | ```sh 16 | cd ruby-example-applications/ruby-magic-link-example && bundle install 17 | ``` 18 | 19 | ## Configure your environment 20 | 21 | 1. Grab your [API Key](https://dashboard.workos.com/api-keys) and your [Client ID](https://dashboard.workos.com/configuration). 22 | 2. Run `cp .env.example .env` and add your API key and Client ID. The `workos` gem will read your API key from the ENV variable `WORKOS_API_KEY` and your Client ID from the ENV variable `WORKOS_CLIENT_ID`. You may also set the API key and Client ID yourself by adding `WorkOS.key = $YOUR_API_KEY` and `CLIENT_ID = $YOUR_CLIENT_ID` to `app.rb`. 23 | 2. Create an [SSO Connection for the Magic Link](https://dashboard.workos.com/sso/connections). 24 | 3. Add a [Redirect URI](https://dashboard.workos.com/sso/configuration) with the value `http://localhost:4567/callback`. 25 | 5. Update `app.rb`: 26 | 27 | ```ruby 28 | REDIRECT_URI = "$YOUR_REDIRECT_URI" 29 | ``` 30 | 31 | ## Run the app and log in using SSO 32 | 33 | ```sh 34 | ruby app.rb 35 | ``` 36 | 37 | Head to `http://localhost:4567` and submit your email. Then, click on the MagicLink 38 | in the email sent from WorkOS to authenticate to the example app. 39 | 40 | For more information, see the [WorkOS Ruby SDK documentation](https://docs.workos.com/sdk/ruby). 41 | -------------------------------------------------------------------------------- /ruby-magic-link-example/app.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'dotenv/load' 4 | require 'sinatra' 5 | require 'workos' 6 | require 'json' 7 | 8 | # Pull API key from ENV variable 9 | WorkOS.key = ENV['WORKOS_API_KEY'] 10 | 11 | # Configure your Redirect URIs on the dashboard configuration 12 | # page: https://dashboard.workos.com/sso/configuration 13 | REDIRECT_URI = 'http://localhost:4567/callback' 14 | 15 | use( 16 | Rack::Session::Cookie, 17 | key: 'rack.session', 18 | domain: 'localhost', 19 | path: '/', 20 | expire_after: 2_592_000, 21 | secret: SecureRandom.hex(16) 22 | ) 23 | 24 | get '/' do 25 | @current_user = session[:user] && JSON.pretty_generate(session[:user]) 26 | @email = session[:email] 27 | print(@email, 'email at /') 28 | erb :index, :layout => :layout 29 | end 30 | 31 | post '/passwordless-auth' do 32 | session = WorkOS::Passwordless.create_session( 33 | email: params[:email], 34 | type: 'MagicLink', 35 | redirect_uri: REDIRECT_URI 36 | ) 37 | WorkOS::Passwordless.send_session(session.id) 38 | email = session.email 39 | magic_link = session.link 40 | redirect "/check-email?email=#{email}&magic_link=#{magic_link}" 41 | end 42 | 43 | get '/check-email' do 44 | email = params[:email] 45 | magic_link = params[:magic_link] 46 | erb :check_email, locals: { email: email, magic_link: magic_link }, layout: :layout 47 | end 48 | 49 | get '/callback' do 50 | profile_and_token = WorkOS::SSO.profile_and_token( 51 | code: params[:code], 52 | client_id: ENV['WORKOS_CLIENT_ID'], 53 | ) 54 | 55 | session[:user] = profile_and_token.profile.to_json 56 | session[:email] = profile_and_token.profile.email 57 | 58 | redirect '/' 59 | end 60 | 61 | get '/logout' do 62 | session[:user] = nil 63 | 64 | redirect '/' 65 | end 66 | -------------------------------------------------------------------------------- /ruby-magic-link-example/public/images/workos_full_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-magic-link-example/public/images/workos_full_logo.png -------------------------------------------------------------------------------- /ruby-magic-link-example/public/images/workos_logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-magic-link-example/public/images/workos_logo_new.png -------------------------------------------------------------------------------- /ruby-magic-link-example/public/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Inter, sans-serif; 3 | background-color: #f9f9fb; 4 | } 5 | 6 | .container_login { 7 | display: flex; 8 | flex-direction: column; 9 | justify-content: center; 10 | margin: auto; 11 | width: 30%; 12 | height: 90vh; 13 | } 14 | 15 | .container_login img { 16 | height: 150px; 17 | } 18 | 19 | .container_login h1 { 20 | font-size: 65px; 21 | color: #111111; 22 | position: relative; 23 | bottom: 10px; 24 | } 25 | 26 | .flex { 27 | display: flex; 28 | justify-content: center; 29 | align-items: center; 30 | } 31 | 32 | .flex_column { 33 | display: flex; 34 | flex-direction: column; 35 | justify-content: center; 36 | align-items: center; 37 | } 38 | 39 | .m-top-20 { 40 | margin-top: 20px; 41 | } 42 | 43 | .width-75 { 44 | width: 75%; 45 | } 46 | 47 | .width-40vw { 48 | width: 40vw; 49 | } 50 | 51 | .width-25vw { 52 | width: 25vw; 53 | } 54 | 55 | .width-18vw { 56 | width: 18vw; 57 | } 58 | 59 | .width-225px { 60 | width: 225px; 61 | } 62 | 63 | .width-941px { 64 | width: 941px; 65 | } 66 | 67 | .width-335 { 68 | width: 335px; 69 | } 70 | 71 | .height-315 { 72 | height: 315px; 73 | } 74 | 75 | .height-40vh { 76 | height: 40vw; 77 | } 78 | 79 | .height-80vh { 80 | height: 80vh; 81 | } 82 | 83 | .height-100vh { 84 | height: 100vh; 85 | } 86 | 87 | .space-between { 88 | justify-content: space-between; 89 | } 90 | 91 | .container_success { 92 | display: flex; 93 | flex-direction: column; 94 | justify-content: center; 95 | margin: auto; 96 | width: 100%; 97 | } 98 | 99 | .heading_div { 100 | margin: 35px 0px 0px 0px; 101 | } 102 | 103 | .heading_text_div { 104 | align-self: center; 105 | } 106 | 107 | .container_success { 108 | display: flex; 109 | flex-direction: column; 110 | justify-content: center; 111 | margin: auto; 112 | width: 100%; 113 | background-size: cover; 114 | } 115 | 116 | .text_input { 117 | border: 1px solid #555555; 118 | border-radius: 10px; 119 | margin: 10px 0px 7px 0px; 120 | padding: 5px; 121 | height: 35px; 122 | text-align: center; 123 | } 124 | 125 | .code-input { 126 | width: 75px; 127 | height: 100px; 128 | margin: 0px 5px 30px 5px; 129 | font-size: 60px; 130 | color: darkslategray; 131 | } 132 | 133 | .qr_div { 134 | align-self: center; 135 | margin-top: 45px; 136 | } 137 | 138 | .qr_code { 139 | width: 7vw; 140 | max-width: 100px; 141 | } 142 | 143 | .factor_card { 144 | border: 1px solid #555555; 145 | border-radius: 10px; 146 | width: 20vw; 147 | margin: 0px 15px 0px 15px; 148 | padding: 25px; 149 | } 150 | 151 | .card { 152 | border: 1px solid #555555; 153 | border-radius: 10px; 154 | margin: 0px 15px 0px 15px; 155 | padding: 25px 50px; 156 | margin-bottom: 20px; 157 | } 158 | 159 | .profile_card { 160 | width: 40vw; 161 | overflow: scroll; 162 | background: white; 163 | box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); 164 | } 165 | 166 | .button { 167 | background-color: #6363f1; 168 | border: 2px solid #6363f1; 169 | border-radius: 10px; 170 | color: white; 171 | padding: 8px 16px; 172 | text-align: center; 173 | text-decoration: none; 174 | display: inline-block; 175 | font-size: 16px; 176 | margin: 4px 2px; 177 | transition-duration: 0.4s; 178 | cursor: pointer; 179 | box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); 180 | } 181 | 182 | .button-outline { 183 | background-color: #f9f9fb; 184 | color: #6363f1; 185 | padding: 8px 16px; 186 | } 187 | 188 | .button-sm { 189 | padding: 8px 16px; 190 | } 191 | 192 | .button:hover, 193 | .button-outline:hover { 194 | background-color: #555555; 195 | border: 2px solid #555555; 196 | color: white; 197 | } 198 | 199 | .sales-button { 200 | margin-left: 10px; 201 | } 202 | 203 | .login_button { 204 | width: 100%; 205 | box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); 206 | background-color: white; 207 | color: ; 208 | } 209 | 210 | .login_button:hover { 211 | border-color: #6363f1; 212 | color: #292929; 213 | } 214 | 215 | h2, 216 | h1 { 217 | text-align: center; 218 | color: #555555; 219 | } 220 | 221 | .logged_in_div_right { 222 | width: 60%; 223 | height: 90vh; 224 | display: flex; 225 | flex-direction: column; 226 | justify-content: center; 227 | align-items: center; 228 | position: relative; 229 | bottom: 10%; 230 | } 231 | 232 | .logged_in_div_left { 233 | width: 40%; 234 | height: 100vh; 235 | display: flex; 236 | flex-direction: column; 237 | justify-content: center; 238 | align-items: left; 239 | background-color: #f9f9fb; 240 | margin-left: 4vw; 241 | } 242 | 243 | .logged_in_div_left div { 244 | justify-content: left; 245 | position: relative; 246 | bottom: 20%; 247 | } 248 | 249 | .logged_in_div_left h1 { 250 | color: #111111; 251 | font-size: 75px; 252 | text-align: left; 253 | margin-bottom: 0px; 254 | font-weight: normal; 255 | letter-spacing: -0.05em; 256 | } 257 | 258 | .home-hero-gradient { 259 | background-image: linear-gradient( 260 | 45deg, 261 | #a163f1, 262 | #6363f1 22%, 263 | #3498ea 40%, 264 | #40dfa3 67%, 265 | rgba(64, 223, 163, 0) 266 | ); 267 | background-size: 150% 100%; 268 | background-repeat: no-repeat; 269 | -webkit-background-clip: text; 270 | -webkit-text-fill-color: transparent; 271 | background-clip: text; 272 | animation: intro-gradient 1.2s cubic-bezier(0.85, 0.26, 0.89, 0.93); 273 | animation-iteration-count: 1; 274 | animation-fill-mode: backwards; 275 | animation-delay: 0.4s; 276 | text-align: left; 277 | font-size: 75px; 278 | letter-spacing: -0.05em; 279 | font-weight: normal; 280 | margin-top: 0px; 281 | } 282 | 283 | .title-text { 284 | margin-bottom: -50px; 285 | } 286 | 287 | .title-subtext { 288 | color: gray; 289 | line-height: 10px; 290 | margin-bottom: 15px; 291 | font-weight: 200; 292 | } 293 | 294 | .logged_in_div_left button { 295 | padding: 8px 22px; 296 | box-shadow: 0px 8px 15px rgba(0, 0, 0, 0.1); 297 | } 298 | 299 | .logged_in_nav { 300 | display: flex; 301 | justify-content: space-between; 302 | background-color: #f9f9fb; 303 | height: 60px; 304 | padding: 15px 30px 15px 30px; 305 | z-index: 1000; 306 | width: 95vw; 307 | } 308 | 309 | .logged_in_nav p { 310 | padding: 4px 0px 0px 15px; 311 | line-height: 1; 312 | color: #29363d; 313 | } 314 | 315 | .logged_in_nav img { 316 | height: 50px; 317 | } 318 | 319 | .nav-item { 320 | color: black; 321 | border: 2px solid #f9f9fb; 322 | background-color: #f9f9fb; 323 | box-shadow: none; 324 | border-radius: 5px; 325 | } 326 | 327 | .blog-nav-button { 328 | margin-right: 20px; 329 | background-color: #f9f9fb; 330 | border: 2px solid #f9f9fb; 331 | } 332 | 333 | .nav-item:hover { 334 | background-color: #f9f9fb; 335 | border: 2px solid #f9f9fb; 336 | color: #a6a4a4; 337 | } 338 | 339 | .workos-logo { 340 | position: relative; 341 | top: 38px; 342 | padding-right: 25px; 343 | height: 75px !important; 344 | } 345 | 346 | .webhooks_container { 347 | width: 45vw; 348 | padding: 25px; 349 | max-height: 450px; 350 | overflow-y: scroll; 351 | } 352 | 353 | .mb-0 { 354 | margin-bottom: 0px; 355 | } 356 | 357 | .mb-20 { 358 | margin-botton: 20px; 359 | } 360 | 361 | .google_button { 362 | background-image: url("./images/google-button.png"); 363 | background-size: cover; 364 | } 365 | 366 | .microsoft_button { 367 | background-image: url("./images/microsoft-button.png"); 368 | background-size: cover; 369 | } 370 | 371 | .saml_button { 372 | background-image: url("./images/saml-button.png"); 373 | background-size: cover; 374 | } 375 | 376 | .error_message { 377 | color: #6363f1; 378 | margin-top: 0px; 379 | font-size: 12px; 380 | } 381 | 382 | #noborder { 383 | border: none; 384 | } 385 | 386 | #noborder > :first-child { 387 | display: none; 388 | } 389 | -------------------------------------------------------------------------------- /ruby-magic-link-example/public/workos_logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-magic-link-example/public/workos_logo_new.png -------------------------------------------------------------------------------- /ruby-magic-link-example/views/check_email.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Here is your Magic Link, <%= email %>

5 | 8 |
9 |
10 |
11 | -------------------------------------------------------------------------------- /ruby-magic-link-example/views/index.erb: -------------------------------------------------------------------------------- 1 | <% if @current_user %> 2 |
3 |
4 |
5 |

Login Successful

6 |
7 |
 8 |                         <%=@current_user %>
 9 |                     
10 |
11 |
12 |
13 |
14 | 15 | <% else %> 16 |
17 |
18 |
19 |
20 |
21 | Log in with Magic Link 22 |
23 |
24 |
25 |
26 |
27 | 34 |
35 |
36 | 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | <% end %> 47 | 48 | -------------------------------------------------------------------------------- /ruby-magic-link-example/views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WorkOS Ruby Magic Link Example 5 | "> 6 | 7 | 8 | 9 | 10 | 11 |
12 |
13 |
14 | " alt="workos logo"> 15 |
16 |
17 |
18 | 19 | 21 | 23 | 24 |
25 |
26 |
27 | <%= yield %> 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /ruby-mfa-example/.env.example: -------------------------------------------------------------------------------- 1 | WORKOS_API_KEY= 2 | WORKOS_CLIENT_ID= -------------------------------------------------------------------------------- /ruby-mfa-example/.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | -------------------------------------------------------------------------------- /ruby-mfa-example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 2 | -------------------------------------------------------------------------------- /ruby-mfa-example/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'dotenv', '2.7.6' 6 | gem 'sinatra', '2.2.0' 7 | gem 'workos', '2.13.0' 8 | -------------------------------------------------------------------------------- /ruby-mfa-example/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | dotenv (2.7.6) 5 | mustermann (1.1.2) 6 | ruby2_keywords (~> 0.0.1) 7 | rack (2.2.6.4) 8 | rack-protection (2.2.0) 9 | rack 10 | ruby2_keywords (0.0.5) 11 | sinatra (2.2.0) 12 | mustermann (~> 1.0) 13 | rack (~> 2.2) 14 | rack-protection (= 2.2.0) 15 | tilt (~> 2.0) 16 | sorbet-runtime (0.5.10741) 17 | tilt (2.1.0) 18 | workos (2.12.0) 19 | sorbet-runtime (~> 0.5) 20 | 21 | PLATFORMS 22 | ruby 23 | 24 | DEPENDENCIES 25 | dotenv (= 2.7.6) 26 | sinatra (= 2.2.0) 27 | workos (= 2.12.0) 28 | 29 | BUNDLED WITH 30 | 2.1.4 31 | -------------------------------------------------------------------------------- /ruby-mfa-example/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 WorkOS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ruby-mfa-example/README.md: -------------------------------------------------------------------------------- 1 | # ruby-mfa-example 2 | 3 | An example Sinatra application demonstrating how MFA works with WorkOS and Ruby. 4 | 5 | ## Clone and Install 6 | 7 | 1. Clone the main repo: 8 | 9 | ```sh 10 | git clone https://github.com/workos/ruby-example-applications.git 11 | ``` 12 | 13 | 2. Navigate to the MFA app within the main repo and install dependencies: 14 | 15 | ```sh 16 | cd ruby-example-applications/ruby-mfa-example && bundle install 17 | ``` 18 | 19 | ## Configure your environment 20 | 21 | 1. Grab your [API Key](https://dashboard.workos.com/api-keys) and your [Client ID](https://dashboard.workos.com/configuration). 22 | 2. Run `cp .env.example .env` and add your API key and Client ID. The `workos` gem will read your API key from the ENV variable `WORKOS_API_KEY` and your Client ID from the ENV variable `WORKOS_CLIENT_ID`. You may also set the API key and Client ID yourself by adding `WorkOS.key = $YOUR_API_KEY` and `CLIENT_ID = $YOUR_CLIENT_ID` to `app.rb`. 23 | 24 | ## Run the app 25 | 26 | ```sh 27 | ruby app.rb 28 | ``` 29 | 30 | Head to `http://localhost:4567` 31 | 32 | When testing SMS use the {{code}} parameter in your string as well as whatever other message details that you'd like to send as this will generate a random code that the user can verify 33 | 34 | For more information, see the [WorkOS Ruby SDK documentation](https://docs.workos.com/sdk/ruby). 35 | -------------------------------------------------------------------------------- /ruby-mfa-example/app.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'dotenv/load' 4 | require 'sinatra' 5 | require 'workos' 6 | require 'json' 7 | 8 | use Rack::Session::Pool 9 | 10 | # Pull API key from ENV variable 11 | WorkOS.key = ENV['WORKOS_API_KEY'] 12 | 13 | get '/' do 14 | if session[:factor_list].nil? 15 | session[:factor_list] ||= [] 16 | @factors = session[:factor_list] 17 | session[:current_factor_qr] = '' 18 | session[:phone_number] = '' 19 | erb :index, :layout => :layout 20 | else 21 | @factors=session[:factor_list] 22 | erb :index, :layout => :layout 23 | end 24 | end 25 | 26 | get '/enroll_factor_details' do 27 | erb :enroll_factor, :layout => :layout 28 | end 29 | 30 | post '/enroll_sms_factor' do 31 | factor_type = params[:type] 32 | phone_number = params[:phone_number] 33 | 34 | new_factor = WorkOS::MFA.enroll_factor( 35 | type: factor_type, 36 | phone_number: phone_number, 37 | ) 38 | session[:factor_list] << new_factor 39 | @factors = session[:factor_list] 40 | redirect '/' 41 | end 42 | 43 | post '/enroll_totp_factor' do 44 | request.body.rewind 45 | parsed_body = JSON.parse(request.body.read) 46 | (type, issuer, user) = parsed_body.values_at('type', 'issuer', 'user') 47 | 48 | new_factor = WorkOS::MFA.enroll_factor( 49 | type: type, 50 | totp_issuer: issuer, 51 | totp_user: user 52 | ) 53 | session[:factor_list] << new_factor 54 | @factors = session[:factor_list] 55 | 56 | content_type :json 57 | return new_factor.totp.to_json 58 | end 59 | 60 | get '/factor_detail' do 61 | factors = session[:factor_list] 62 | @factor = factors.select {|factor| factor.id == params[:id] }.first 63 | if @factor.type == 'sms' 64 | @phone_number = @factor .sms[:phone_number] 65 | session[:phone_numer] = @phone_number 66 | elsif @factor.type == 'totp' 67 | @current_factor_qr = @factor.totp[:qr_code] 68 | session[:current_factor_qr] = @current_factor_qr 69 | end 70 | session[:current_factor] = @factor.id 71 | session[:current_factor_type] = @factor.type 72 | erb :factor_detail, :layout => :layout 73 | end 74 | 75 | post '/challenge_factor' do 76 | if session[:current_factor_type] == 'sms' 77 | message = params[:sms_message] 78 | session[:message] = message 79 | challenge = WorkOS::MFA.challenge_factor( 80 | authentication_factor_id: session[:current_factor], 81 | sms_template: message, 82 | ) 83 | else 84 | challenge = WorkOS::MFA.challenge_factor( 85 | authentication_factor_id: session[:current_factor], 86 | ) 87 | end 88 | session[:challenge_id] = challenge.id 89 | 90 | erb :challenge_factor, :layout => :layout 91 | end 92 | 93 | post '/verify_factor' do 94 | code = params.values.join("") 95 | challenge_id = session[:challenge_id] 96 | verify_factor = WorkOS::MFA.verify_factor( 97 | authentication_challenge_id: challenge_id, 98 | code: code 99 | ) 100 | @challenge = verify_factor.challenge 101 | @valid = verify_factor.valid 102 | @type = session[:type] 103 | erb :challenge_success, :layout => :layout 104 | end 105 | 106 | get '/clear_session' do 107 | session.clear 108 | redirect '/' 109 | end -------------------------------------------------------------------------------- /ruby-mfa-example/public/images/settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ruby-mfa-example/public/images/workos_full_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-mfa-example/public/images/workos_full_logo.png -------------------------------------------------------------------------------- /ruby-mfa-example/public/images/workos_logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-mfa-example/public/images/workos_logo_new.png -------------------------------------------------------------------------------- /ruby-mfa-example/views/challenge_factor.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 | 8 | 10 | 12 | 14 | 16 | 18 |
19 |
20 |
21 | 22 |
23 |
24 | 26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 52 | -------------------------------------------------------------------------------- /ruby-mfa-example/views/challenge_success.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

User Verified: <%=@valid%>

5 |

Factor ID: <%=@challenge[:authentication_factor_id]%>

6 |

Created At: <%=@challenge[:created_at]%>

7 | <%if @type == 'sms'%> 8 |

Expires At: <%=@challenge[:expires_at]%>

9 | <% end %> 10 |
11 |
12 | 13 |
14 | -------------------------------------------------------------------------------- /ruby-mfa-example/views/enroll_factor.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Enroll SMS Factor

5 |
6 |
7 | 9 |
10 | 13 |
14 |
15 |
16 |

Enroll TOTP Factor

17 |
18 |
19 | 21 |
22 |
23 | 25 |
26 | 27 |
28 | 30 |
31 |
32 |
33 |
34 |
35 | 40 | 41 | 100 | 101 | -------------------------------------------------------------------------------- /ruby-mfa-example/views/factor_detail.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

Factor Details

5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
ID:<%= @factor.id %>
Type:<%= @factor.type %>
Phone Number:<%= @phone_number %>
Created At:<%= @factor.created_at %>
Updated At:<%= @factor.updated_at %>
27 |
28 |
29 |
30 |
31 |
32 | <% if @factor.type == 'sms' %> 33 |
34 |
35 | 43 |
44 |
45 | 48 |
49 |
50 | <% else %> 51 |
52 | 55 |
56 | <% end %> 57 |
58 |
59 |
60 |
-------------------------------------------------------------------------------- /ruby-mfa-example/views/index.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 | 22 |
23 |
24 | <%if @factors.any? %> 25 | 26 | 27 | 28 | 29 | 30 | 31 | <% @factors.each do |factor| %> 32 | 33 | 34 | 35 | 43 | 44 | <%end%> 45 |
TypeIDView Details
<%=factor.type %><%=factor.id%> 36 |
" alt="settings">
41 |
42 |
46 | <%else%> 47 |
48 |

No Current Factors

49 |
50 | <%end%> 51 |
52 |
53 |
54 | -------------------------------------------------------------------------------- /ruby-mfa-example/views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | > 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 | " alt="workos logo"> 14 |
15 |
16 |
17 | 18 | 20 | 22 | 23 |
24 |
25 |
26 |
27 | <%= yield %> 28 |
29 |
30 | 31 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/.env.example: -------------------------------------------------------------------------------- 1 | WORKOS_API_KEY= 2 | WORKOS_CLIENT_ID= 3 | WORKOS_REDIRECT_URI=http://localhost:5000/sso/callback 4 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | /db/*.sqlite3-* 14 | 15 | # Ignore all logfiles and tempfiles. 16 | /log/* 17 | /tmp/* 18 | !/log/.keep 19 | !/tmp/.keep 20 | 21 | # Ignore pidfiles, but keep the directory. 22 | /tmp/pids/* 23 | !/tmp/pids/ 24 | !/tmp/pids/.keep 25 | 26 | # Ignore uploaded files in development. 27 | /storage/* 28 | !/storage/.keep 29 | 30 | /public/assets 31 | .byebug_history 32 | 33 | # Ignore master key for decrypting credentials and more. 34 | /config/master.key 35 | 36 | /public/packs 37 | /public/packs-test 38 | /node_modules 39 | /yarn-error.log 40 | yarn-debug.log* 41 | .yarn-integrity 42 | 43 | # Ignore environment variables 44 | .env 45 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 2 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | ruby '2.7.2' 5 | 6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 7 | gem 'rails', '~> 7.0.1' 8 | # Use sqlite3 as the database for Active Record 9 | gem 'sqlite3', '~> 1.4' 10 | # Use Puma as the app server 11 | gem 'puma', '~> 4.3' 12 | # Use SCSS for stylesheets 13 | gem 'sass-rails', '>= 6' 14 | # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker 15 | gem 'webpacker', '~> 4.0' 16 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks 17 | gem 'turbolinks', '~> 5' 18 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 19 | gem 'jbuilder', '~> 2.7' 20 | gem "sprockets-rails" 21 | gem "net-http" 22 | gem "foreman" 23 | gem 'bootstrap', '~> 5.1.1' 24 | # Use Redis adapter to run Action Cable in production 25 | # gem 'redis', '~> 4.0' 26 | # Use Active Model has_secure_password 27 | # gem 'bcrypt', '~> 3.1.7' 28 | 29 | # Use Active Storage variant 30 | # gem 'image_processing', '~> 1.2' 31 | 32 | # Use Devise for general authentication 33 | gem 'devise' 34 | # Use WorkOS to authenticate users via SSO 35 | gem 'workos', '2.13.0' 36 | # Use JSON to generate user information in JSON format 37 | gem "json", ">= 2.3.0" 38 | # Reduces boot times through caching; required in config/boot.rb 39 | gem 'bootsnap', '>= 1.4.2', require: false 40 | 41 | group :development, :test do 42 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 43 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 44 | end 45 | 46 | group :development do 47 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code. 48 | gem 'web-console', '>= 3.3.0' 49 | gem 'listen', '~> 3.2' 50 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 51 | gem 'spring' 52 | gem 'spring-watcher-listen', '~> 2.0.0' 53 | end 54 | 55 | group :test do 56 | # Adds support for Capybara system testing and selenium driver 57 | gem 'capybara', '>= 2.15' 58 | gem 'selenium-webdriver' 59 | # Easy installation and use of web drivers to run system tests with browsers 60 | gem 'webdrivers' 61 | end 62 | 63 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 64 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 65 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/Procfile.dev: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb 2 | webpacker: ./bin/webpack-dev-server 3 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/README.md: -------------------------------------------------------------------------------- 1 | # rails-sso-example 2 | 3 | An example Ruby on Rails application demonstrating how SSO works with WorkOS and Rails. 4 | The application is built with Rails 6 and Bootstrap with Webpack, and also uses Devise. 5 | 6 | ## Get Started 7 | 8 | ### Requirements 9 | 10 | - Ruby 2.7.2 11 | - Rails 7 12 | - Foreman gem 13 | 14 | ### Clone, install and migrate the database 15 | 16 | ```bash 17 | git clone https://github.com/workos/ruby-example-applications.git 18 | cd ruby-example-applications/ruby-rails-sso-example 19 | bundle install 20 | yarn install --check-files 21 | rails db:migrate 22 | ``` 23 | 24 | ## Set up SSO Connection with WorkOS 25 | 26 | Use the [WorkOS documentation](https://workos.com/docs/sso/guide/introduction) to set up an SSO connection with your identity provider of choice. 27 | 28 | ### Setup in the WorkOS Dashboard 29 | 30 | You'll need to create an [Organization](https://dashboard.workos.com/organizations) and an SSO Connection in the Organization in your WorkOS Dashboard. Additionally, add a [Redirect URI](https://dashboard.workos.com/configuration) with the value `http://localhost:5000/sso/callback`. 31 | 32 | ### Setup environment variables 33 | 34 | Run `cp .env.example .env` and add your [API Key](https://dashboard.workos.com/api-keys) and [Client ID](https://dashboard.workos.com/configuration). The `workos` gem will read your API key from the ENV variable `WORKOS_API_KEY` and your Client ID from the ENV variable `WORKOS_CLIENT_ID`. You may also set the API key and Client ID yourself by adding `WorkOS.key = $YOUR_API_KEY` and `CLIENT_ID = $YOUR_CLIENT_ID` to `sessions_controller.rb`. 35 | 36 | Additionally, you'll want to set the `ORGANIZATION_ID` in the `sessions_controller.rb` for the Connection you are testing. 37 | 38 | ## Run the application and sign in using SSO 39 | 40 | Start the server: 41 | 42 | ```bash 43 | foreman start -f Procfile.dev 44 | ``` 45 | 46 | ### Application Flow 47 | 48 | - Head to `http://localhost:5000` 49 | - If you're not authenticated, the site will re-direct to `http://localhost:5000/users/sign_in` 50 | - Here you can authenticate with Username/Password, or with the SSO you set up with WorkOS 51 | - To authenticate with SSO, input the domain you used to set up your WorkOS connection, and select the `Sign in with SSO` button 52 | - After successfully authenticating, you should see a JSON print out of your user information 53 | 54 | For more information, see the [WorkOS Ruby SDK documentation](https://docs.workos.com/sdk/ruby). 55 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-rails-sso-example/app/assets/images/.keep -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/assets/images/google-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-rails-sso-example/app/assets/images/google-button.png -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/assets/images/microsoft-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-rails-sso-example/app/assets/images/microsoft-button.png -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/assets/images/saml-button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-rails-sso-example/app/assets/images/saml-button.png -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/assets/images/workos-logo-with-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-rails-sso-example/app/assets/images/workos-logo-with-text.png -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/assets/images/workos_logo_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-rails-sso-example/app/assets/images/workos_logo_new.png -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | before_action :configure_permitted_parameters, if: :devise_controller? 4 | before_action :authenticate_user! 5 | 6 | def home; end 7 | 8 | protected 9 | 10 | def configure_permitted_parameters 11 | devise_parameter_sanitizer.permit(:sign_up) 12 | devise_parameter_sanitizer.permit(:account_update) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-rails-sso-example/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/controllers/users/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Users::RegistrationsController < Devise::RegistrationsController 4 | 5 | # The path used after sign up. 6 | def after_sign_up_path_for(resource) 7 | root_path 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/controllers/users/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | class Users::SessionsController < Devise::SessionsController 3 | 4 | # Define WorkOS API key and Client ID from environment variables 5 | WorkOS.key = ENV['WORKOS_API_KEY'] 6 | CLIENT_ID = ENV['WORKOS_CLIENT_ID'] 7 | 8 | # Set the Organization ID that you want to test 9 | ORGANIZATION_ID = 'CHANGE TO YOUR ORGANIZATION' 10 | 11 | # GET /sso/new path to authenticate via WorkOS 12 | # You can also use connection or provider parameters 13 | # in place of the domain parameter 14 | # https://workos.com/docs/reference/sso/authorize/get 15 | def auth 16 | login_type = params[:login_method] 17 | params = { 18 | client_id: CLIENT_ID, 19 | redirect_uri: ENV['WORKOS_REDIRECT_URI'], 20 | state: "" 21 | } 22 | 23 | if login_type == 'saml' 24 | params[:organization] = ORGANIZATION_ID 25 | else 26 | params[:provider] = login_type 27 | end 28 | 29 | authorization_url = WorkOS::SSO.authorization_url(**params) 30 | redirect_to authorization_url 31 | end 32 | 33 | # GET /sso/callback path to consume profile object from WorkOS 34 | def callback 35 | profile_and_token = WorkOS::SSO.profile_and_token( 36 | code: params['code'], 37 | client_id: CLIENT_ID, 38 | ) 39 | @user = User.from_sso(profile_and_token.profile) 40 | @user.save 41 | puts User.all 42 | sign_in_and_redirect @user 43 | end 44 | 45 | # Send user to root path after authenticating 46 | def after_sign_in_path_for(_resource) 47 | root_path 48 | end 49 | 50 | def destroy 51 | sign_out_and_redirect(current_user) 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/javascript/channels/consumer.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | 4 | import { createConsumer } from "@rails/actioncable" 5 | 6 | export default createConsumer() 7 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/javascript/channels/index.js: -------------------------------------------------------------------------------- 1 | // Load all the channels within this directory and all subdirectories. 2 | // Channel files must be named *_channel.js. 3 | 4 | const channels = require.context('.', true, /_channel\.js$/) 5 | channels.keys().forEach(channels) 6 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/javascript/packs/application.js: -------------------------------------------------------------------------------- 1 | // This file is automatically compiled by Webpack, along with any other files 2 | // present in this directory. You're encouraged to place your actual application logic in 3 | // a relevant structure within app/javascript and only use these pack files to reference 4 | // that code so it'll be compiled. 5 | 6 | require("@rails/ujs").start() 7 | // require("turbolinks").start() 8 | require("@rails/activestorage").start() 9 | require("channels") 10 | 11 | import '../stylesheets/application' 12 | import 'bootstrap/dist/js/bootstrap' 13 | // Uncomment to copy all static images under ../images to the output folder and reference 14 | // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>) 15 | // or the `imagePath` JavaScript helper below. 16 | // 17 | // const images = require.context('../images', true) 18 | // const imagePath = (name) => images(name, true) 19 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/javascript/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | @import '~bootstrap/scss/bootstrap.scss'; 2 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/workos/ruby-example-applications/7f7ead65890db5a6c3b43165c6c5fa10a548c9c9/ruby-rails-sso-example/app/models/concerns/.keep -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | # Include default devise modules. Others available are: 3 | # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable 4 | devise :database_authenticatable, :registerable, 5 | :recoverable, :rememberable, :validatable 6 | 7 | serialize :raw_attributes 8 | 9 | def self.from_sso(profile) 10 | where(provider: profile.connection_type, uid: profile.id).first_or_create do |user| 11 | user.email = profile.email 12 | user.password = Devise.friendly_token[0, 20] 13 | user.first_name = profile.first_name 14 | user.last_name = profile.last_name 15 | user.raw_attributes = profile.raw_attributes 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/views/application/home.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= image_tag("workos-logo-with-text.png", :alt => "WorkOS Logo") %>
5 |
6 | 7 |
8 |
9 | 10 | 12 | 14 | 15 |
16 | 17 |
18 |
19 |
20 |
21 |
22 |

Profile Details

23 |
24 |
25 | <%= form_with url: sign_out_path, method: :delete, local: true do |f| %> 26 | <%= f.submit "Sign Out", class: 'button button-outline' %>
27 | <% end %> 28 |
29 |
30 |
31 |                     <%= JSON.pretty_generate(current_user.attributes) %>
32 |                 
33 |
34 |
35 |
36 |
37 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Rails SSO Example App 5 | <%= csrf_meta_tags %> 6 | <%= csp_meta_tag %> 7 | <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> 8 | <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> 9 | 10 | <%= stylesheet_link_tag "application", media: "all" %> 11 | 12 | 13 | 14 |

<%= notice %>

15 |

<%= alert %>

16 | <%= yield %> 17 | 18 | 19 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/views/users/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= resource_name.to_s.humanize %>

2 | 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= render "users/shared/error_messages", resource: resource %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, autocomplete: "email" %> 9 |
10 | 11 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> 12 |
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
13 | <% end %> 14 | 15 |
16 | <%= f.label :password %> (leave blank if you don't want to change it)
17 | <%= f.password_field :password, autocomplete: "new-password" %> 18 | <% if @minimum_password_length %> 19 |
20 | <%= @minimum_password_length %> characters minimum 21 | <% end %> 22 |
23 | 24 |
25 | <%= f.label :password_confirmation %>
26 | <%= f.password_field :password_confirmation, autocomplete: "new-password" %> 27 |
28 | 29 |
30 | <%= f.label :current_password %> (we need your current password to confirm your changes)
31 | <%= f.password_field :current_password, autocomplete: "current-password" %> 32 |
33 | 34 |
35 | <%= f.submit "Update" %> 36 |
37 | <% end %> 38 | 39 |

Cancel my account

40 | 41 |

Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>

42 | 43 | <%= link_to "Back", :back %> 44 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/views/users/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= image_tag("workos-logo-with-text.png", :alt => "WorkOS Logo") %>
5 |
6 |
7 | 8 | 10 | 12 | 13 |
14 |
15 |
16 |
17 | 18 |

Sign up

19 |
20 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> 21 | <%= render "devise/shared/error_messages", resource: resource %> 22 | 23 |
24 | <%= f.label :email %>
25 | <%= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control m-2' %> 26 |
27 | 28 |
29 | <%= f.label :password %> 30 | <% if @minimum_password_length %> 31 | (<%= @minimum_password_length %> characters minimum) 32 | <% end %>
33 | <%= f.password_field :password, autocomplete: "new-password", class: 'form-control m-2' %> 34 |
35 | 36 |
37 | <%= f.label :password_confirmation %>
38 | <%= f.password_field :password_confirmation, autocomplete: "new-password", class: 'form-control m-2' %> 39 |
40 | 41 |
42 | <%= f.submit "Sign up", class: 'button' %> 43 |
44 | <% end %> 45 |
46 | <%= render "users/shared/links" %> 47 |
48 |
49 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/views/users/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= image_tag("workos-logo-with-text.png", :alt => "WorkOS Logo") %>
5 |
6 |
7 | 8 | 10 | 12 | 13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 | 30 |
31 |
32 | 33 |
34 |
35 | <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> 36 |
37 | <%= f.label :email %>
38 | <%= f.email_field :email, autofocus: true, autocomplete: "email", class: 'form-control' %> 39 |
40 | 41 |
42 | <%= f.label :password %>
43 | <%= f.password_field :password, autocomplete: "current-password", class: 'form-control' %> 44 |
45 | 46 | <% if devise_mapping.rememberable? %> 47 |
48 | <%= f.check_box :remember_me, class: 'm-2' %> 49 | <%= f.label :remember_me %> 50 |
51 | <% end %> 52 | <% end %> 53 |
54 | <%= render "users/shared/links" %> 55 |
56 |
57 | <%= form_with url: sso_new_path, method: :get, local: true do |f| %> 58 | 59 |
60 | 63 | 66 | 69 | <% end %> 70 |
71 |
72 |
73 |
74 |
75 |
76 | 77 |
78 |
79 |
-------------------------------------------------------------------------------- /ruby-rails-sso-example/app/views/users/shared/_error_messages.html.erb: -------------------------------------------------------------------------------- 1 | <% if resource.errors.any? %> 2 |
3 |

4 | <%= I18n.t("errors.messages.not_saved", 5 | count: resource.errors.count, 6 | resource: resource.class.model_name.human.downcase) 7 | %> 8 |

9 |
    10 | <% resource.errors.full_messages.each do |message| %> 11 |
  • <%= message %>
  • 12 | <% end %> 13 |
14 |
15 | <% end %> 16 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/app/views/users/shared/_links.html.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Log in", new_session_path(resource_name) %>
3 | <% end %> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end %> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end %> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end %> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end %> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end %> 25 | <% end %> 26 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function(api) { 2 | var validEnv = ['development', 'test', 'production'] 3 | var currentEnv = api.env() 4 | var isDevelopmentEnv = api.env('development') 5 | var isProductionEnv = api.env('production') 6 | var isTestEnv = api.env('test') 7 | 8 | if (!validEnv.includes(currentEnv)) { 9 | throw new Error( 10 | 'Please specify a valid `NODE_ENV` or ' + 11 | '`BABEL_ENV` environment variables. Valid values are "development", ' + 12 | '"test", and "production". Instead, received: ' + 13 | JSON.stringify(currentEnv) + 14 | '.' 15 | ) 16 | } 17 | 18 | return { 19 | presets: [ 20 | isTestEnv && [ 21 | '@babel/preset-env', 22 | { 23 | targets: { 24 | node: 'current' 25 | } 26 | } 27 | ], 28 | (isProductionEnv || isDevelopmentEnv) && [ 29 | '@babel/preset-env', 30 | { 31 | forceAllTransforms: true, 32 | useBuiltIns: 'entry', 33 | corejs: 3, 34 | modules: false, 35 | exclude: ['transform-typeof-symbol'] 36 | } 37 | ] 38 | ].filter(Boolean), 39 | plugins: [ 40 | 'babel-plugin-macros', 41 | '@babel/plugin-syntax-dynamic-import', 42 | isTestEnv && 'babel-plugin-dynamic-import-node', 43 | '@babel/plugin-transform-destructuring', 44 | [ 45 | '@babel/plugin-proposal-class-properties', 46 | { 47 | loose: true 48 | } 49 | ], 50 | [ 51 | '@babel/plugin-proposal-object-rest-spread', 52 | { 53 | useBuiltIns: true 54 | } 55 | ], 56 | [ 57 | '@babel/plugin-transform-runtime', 58 | { 59 | helpers: false, 60 | regenerator: true, 61 | corejs: false 62 | } 63 | ], 64 | [ 65 | '@babel/plugin-transform-regenerator', 66 | { 67 | async: false 68 | } 69 | ] 70 | ].filter(Boolean) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'bundle' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "rubygems" 12 | 13 | m = Module.new do 14 | module_function 15 | 16 | def invoked_as_script? 17 | File.expand_path($0) == File.expand_path(__FILE__) 18 | end 19 | 20 | def env_var_version 21 | ENV["BUNDLER_VERSION"] 22 | end 23 | 24 | def cli_arg_version 25 | return unless invoked_as_script? # don't want to hijack other binstubs 26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` 27 | bundler_version = nil 28 | update_index = nil 29 | ARGV.each_with_index do |a, i| 30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN 31 | bundler_version = a 32 | end 33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ 34 | bundler_version = $1 || ">= 0.a" 35 | update_index = i 36 | end 37 | bundler_version 38 | end 39 | 40 | def gemfile 41 | gemfile = ENV["BUNDLE_GEMFILE"] 42 | return gemfile if gemfile && !gemfile.empty? 43 | 44 | File.expand_path("../../Gemfile", __FILE__) 45 | end 46 | 47 | def lockfile 48 | lockfile = 49 | case File.basename(gemfile) 50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) 51 | else "#{gemfile}.lock" 52 | end 53 | File.expand_path(lockfile) 54 | end 55 | 56 | def lockfile_version 57 | return unless File.file?(lockfile) 58 | lockfile_contents = File.read(lockfile) 59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ 60 | Regexp.last_match(1) 61 | end 62 | 63 | def bundler_version 64 | @bundler_version ||= begin 65 | env_var_version || cli_arg_version || 66 | lockfile_version || "#{Gem::Requirement.default}.a" 67 | end 68 | end 69 | 70 | def load_bundler! 71 | ENV["BUNDLE_GEMFILE"] ||= gemfile 72 | 73 | # must dup string for RG < 1.8 compatibility 74 | activate_bundler(bundler_version.dup) 75 | end 76 | 77 | def activate_bundler(bundler_version) 78 | if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0") 79 | bundler_version = "< 2" 80 | end 81 | gem_error = activation_error_handling do 82 | gem "bundler", bundler_version 83 | end 84 | return if gem_error.nil? 85 | require_error = activation_error_handling do 86 | require "bundler/version" 87 | end 88 | return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION)) 89 | warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`" 90 | exit 42 91 | end 92 | 93 | def activation_error_handling 94 | yield 95 | nil 96 | rescue StandardError, LoadError => e 97 | e 98 | end 99 | end 100 | 101 | m.load_bundler! 102 | 103 | if m.invoked_as_script? 104 | load Gem.bin_path("bundler", "bundle") 105 | end 106 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path("..", __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?("config/database.yml") 22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! "bin/rails db:prepare" 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! "bin/rails log:clear tmp:clear" 30 | 31 | puts "\n== Restarting application server ==" 32 | system! "bin/rails restart" 33 | end 34 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads Spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == 'spring' } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/bin/webpack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "bundler/setup" 11 | 12 | require "webpacker" 13 | require "webpacker/webpack_runner" 14 | 15 | APP_ROOT = File.expand_path("..", __dir__) 16 | Dir.chdir(APP_ROOT) do 17 | Webpacker::WebpackRunner.run(ARGV) 18 | end 19 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/bin/webpack-dev-server: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" 4 | ENV["NODE_ENV"] ||= "development" 5 | 6 | require "pathname" 7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 8 | Pathname.new(__FILE__).realpath) 9 | 10 | require "bundler/setup" 11 | 12 | require "webpacker" 13 | require "webpacker/dev_server_runner" 14 | 15 | APP_ROOT = File.expand_path("..", __dir__) 16 | Dir.chdir(APP_ROOT) do 17 | Webpacker::DevServerRunner.run(ARGV) 18 | end 19 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | begin 5 | exec "yarnpkg", *ARGV 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails/all" 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module RailsSsoExample 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 6.0 13 | config.active_record.yaml_column_permitted_classes = [Symbol, Hash, Array, ActiveSupport::HashWithIndifferentAccess] 14 | 15 | # Configuration for the application, engines, and railties goes here. 16 | # 17 | # These settings can be overridden in specific environments using the files 18 | # in config/environments, which are processed later. 19 | # 20 | # config.time_zone = "Central Time (US & Canada)" 21 | # config.eager_load_paths << Rails.root.join("extras") 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: rails_sso_example_production 11 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | JeX/tseNigyxe6IL4ANH3TrVFim4tP/gQ2eIQ7PQO9v/y2JuECZhRhfeinvxdnZq79jyf1uMp4h4l+SNVC19tSrC69VDKohwbpiCwsEI5IgFoxEJwK3TiES5Q9L/Q1iGdYrlG0thKhrM9tyA794Qb/zQox7Im64EMc3gGwxDYZm2vIUwEfJ6ThqaGVMvOTIwmycqhCHomviEdJx6Ckb8ttrSeLVfdQJ7soZUhUMj/3YYvNMoLKRCzIwy6uYZeXKTRFpmSjJLyHLG1GRBlTRjMZaMcf39NoKuuzjWC08t+TCOp3/5oyc8NaxK4JRRQPBWe8HpfWuwiIaBkl0OXX/xn6SYIvNPHxbJomDwkEyVc7taaakdwjr/JkEJVYb1DyuBhjvMt22dpHTler5BXRYGaiy9SRJKtUhF7CGa--x/avyWikm9/MhKwP--UTdvCcsYl1h4mogu8VkeZA== -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded any time 7 | # it changes. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.cache_classes = false 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports. 15 | config.consider_all_requests_local = true 16 | 17 | # Enable server timing 18 | config.server_timing = true 19 | 20 | # Enable/disable caching. By default caching is disabled. 21 | # Run rails dev:cache to toggle caching. 22 | if Rails.root.join("tmp/caching-dev.txt").exist? 23 | config.action_controller.perform_caching = true 24 | config.action_controller.enable_fragment_cache_logging = true 25 | 26 | config.cache_store = :memory_store 27 | config.public_file_server.headers = { 28 | "Cache-Control" => "public, max-age=#{2.days.to_i}" 29 | } 30 | else 31 | config.action_controller.perform_caching = false 32 | 33 | config.cache_store = :null_store 34 | end 35 | 36 | # Store uploaded files on the local file system (see config/storage.yml for options). 37 | config.active_storage.service = :local 38 | 39 | # Don't care if the mailer can't send. 40 | config.action_mailer.raise_delivery_errors = false 41 | 42 | config.action_mailer.perform_caching = false 43 | 44 | # Print deprecation notices to the Rails logger. 45 | config.active_support.deprecation = :log 46 | 47 | # Raise exceptions for disallowed deprecations. 48 | config.active_support.disallowed_deprecation = :raise 49 | 50 | # Tell Active Support which deprecation messages to disallow. 51 | config.active_support.disallowed_deprecation_warnings = [] 52 | 53 | # Raise an error on page load if there are pending migrations. 54 | config.active_record.migration_error = :page_load 55 | 56 | # Highlight code that triggered database queries in logs. 57 | config.active_record.verbose_query_logs = true 58 | 59 | # Suppress logger output for asset requests. 60 | config.assets.quiet = true 61 | 62 | # Raises error for missing translations. 63 | # config.i18n.raise_on_missing_translations = true 64 | 65 | # Annotate rendered view with file names. 66 | # config.action_view.annotate_rendered_view_with_filenames = true 67 | 68 | # Uncomment if you wish to allow Action Cable access from any origin. 69 | # config.action_cable.disable_request_forgery_protection = true 70 | end 71 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.cache_classes = true 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 21 | # config.require_master_key = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? 26 | 27 | # Compress CSS using a preprocessor. 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 34 | # config.asset_host = "http://assets.example.com" 35 | 36 | # Specifies the header that your server uses for sending files. 37 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache 38 | # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX 39 | 40 | # Store uploaded files on the local file system (see config/storage.yml for options). 41 | config.active_storage.service = :local 42 | 43 | # Mount Action Cable outside main process or domain. 44 | # config.action_cable.mount_path = nil 45 | # config.action_cable.url = "wss://example.com/cable" 46 | # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] 47 | 48 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 49 | # config.force_ssl = true 50 | 51 | # Include generic and useful information about system operation, but avoid logging too much 52 | # information to avoid inadvertent exposure of personally identifiable information (PII). 53 | config.log_level = :info 54 | 55 | # Prepend all log lines with the following tags. 56 | config.log_tags = [ :request_id ] 57 | 58 | # Use a different cache store in production. 59 | # config.cache_store = :mem_cache_store 60 | 61 | # Use a real queuing backend for Active Job (and separate queues per environment). 62 | # config.active_job.queue_adapter = :resque 63 | # config.active_job.queue_name_prefix = "rails_sso_example_production" 64 | 65 | config.action_mailer.perform_caching = false 66 | 67 | # Ignore bad email addresses and do not raise email delivery errors. 68 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 69 | # config.action_mailer.raise_delivery_errors = false 70 | 71 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 72 | # the I18n.default_locale when a translation cannot be found). 73 | config.i18n.fallbacks = true 74 | 75 | # Don't log any deprecations. 76 | config.active_support.report_deprecations = false 77 | 78 | # Use default logging formatter so that PID and timestamp are not suppressed. 79 | config.log_formatter = ::Logger::Formatter.new 80 | 81 | # Use a different logger for distributed setups. 82 | # require "syslog/logger" 83 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new "app-name") 84 | 85 | if ENV["RAILS_LOG_TO_STDOUT"].present? 86 | logger = ActiveSupport::Logger.new(STDOUT) 87 | logger.formatter = config.log_formatter 88 | config.logger = ActiveSupport::TaggedLogging.new(logger) 89 | end 90 | 91 | # Do not dump schema after migrations. 92 | config.active_record.dump_schema_after_migration = false 93 | end 94 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | # The test environment is used exclusively to run your application's 4 | # test suite. You never need to work with it otherwise. Remember that 5 | # your test database is "scratch space" for the test suite and is wiped 6 | # and recreated between test runs. Don't rely on the data there! 7 | 8 | Rails.application.configure do 9 | # Settings specified here will take precedence over those in config/application.rb. 10 | 11 | # Turn false under Spring and add config.action_view.cache_template_loading = true. 12 | config.cache_classes = true 13 | 14 | # Eager loading loads your whole application. When running a single test locally, 15 | # this probably isn't necessary. It's a good idea to do in a continuous integration 16 | # system, or in some way before deploying your code. 17 | config.eager_load = ENV["CI"].present? 18 | 19 | # Configure public file server for tests with Cache-Control for performance. 20 | config.public_file_server.enabled = true 21 | config.public_file_server.headers = { 22 | "Cache-Control" => "public, max-age=#{1.hour.to_i}" 23 | } 24 | 25 | # Show full error reports and disable caching. 26 | config.consider_all_requests_local = true 27 | config.action_controller.perform_caching = false 28 | config.cache_store = :null_store 29 | 30 | # Raise exceptions instead of rendering exception templates. 31 | config.action_dispatch.show_exceptions = false 32 | 33 | # Disable request forgery protection in test environment. 34 | config.action_controller.allow_forgery_protection = false 35 | 36 | # Store uploaded files on the local file system in a temporary directory. 37 | config.active_storage.service = :test 38 | 39 | config.action_mailer.perform_caching = false 40 | 41 | # Tell Action Mailer not to deliver emails to the real world. 42 | # The :test delivery method accumulates sent emails in the 43 | # ActionMailer::Base.deliveries array. 44 | config.action_mailer.delivery_method = :test 45 | 46 | # Print deprecation notices to the stderr. 47 | config.active_support.deprecation = :stderr 48 | 49 | # Raise exceptions for disallowed deprecations. 50 | config.active_support.disallowed_deprecation = :raise 51 | 52 | # Tell Active Support which deprecation messages to disallow. 53 | config.active_support.disallowed_deprecation_warnings = [] 54 | 55 | # Raises error for missing translations. 56 | # config.i18n.raise_on_missing_translations = true 57 | 58 | # Annotate rendered view with file names. 59 | # config.action_view.annotate_rendered_view_with_filenames = true 60 | end 61 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in the app/assets 11 | # folder are already added. 12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 13 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.configure do 8 | # config.content_security_policy do |policy| 9 | # policy.default_src :self, :https 10 | # policy.font_src :self, :https, :data 11 | # policy.img_src :self, :https, :data 12 | # policy.object_src :none 13 | # policy.script_src :self, :https 14 | # policy.style_src :self, :https 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | # 19 | # # Generate session nonces for permitted importmap and inline scripts 20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } 21 | # config.content_security_policy_nonce_directives = %w(script-src) 22 | # 23 | # # Report CSP violations to a specified URI. See: 24 | # # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 25 | # # config.content_security_policy_report_only = true 26 | # end 27 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be filtered from the log file. Use this to limit dissemination of 4 | # sensitive information. See the ActiveSupport::ParameterFilter documentation for supported 5 | # notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 8 | ] 9 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /ruby-rails-sso-example/config/initializers/new_framework_defaults_7_0.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file eases your Rails 7.0 framework defaults upgrade. 4 | # 5 | # Uncomment each configuration one by one to switch to the new default. 6 | # Once your application is ready to run with all new defaults, you can remove 7 | # this file and set the `config.load_defaults` to `7.0`. 8 | # 9 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 10 | # https://guides.rubyonrails.org/upgrading_ruby_on_rails.html 11 | 12 | # `button_to` view helper will render ` 12 | 14 | 16 | 17 | 18 | 19 |
20 |
21 |
22 |
23 |
24 |

Profile Details

25 |
26 |
27 | 28 |
29 |
30 |
31 |
32 |                     <%= @current_user %>
33 |                 
34 |
35 |
36 |
37 |
38 | <% else %> 39 |
40 |
41 |
42 | " alt="workos logo"> 43 |
44 |
45 |
46 | 47 | 49 | 51 | 52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Log in with SSO 61 |
62 |
63 | 66 | 69 | 72 |
73 |
74 |
75 |
76 |
77 | <% end %> -------------------------------------------------------------------------------- /ruby-sso-example/views/layout.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WorkOS Ruby SSO Example 5 | "> 6 | 7 | 8 | 9 | 10 |
11 | <%= yield %> 12 |
13 | 14 | --------------------------------------------------------------------------------