class Api::V1::IdentitiesController < ApplicationController before_action :authenticate_for_identity! attr_reader :provider, :identity, :user, :access_token, :refresh_token # POST /identities/:provider def provider_create @provider = params[:provider] # We want to make sure we support the provider if Identity.valid_provider?(provider) set_identity return unless set_user # If the identity exists, we want to update it if identity.present? identity.update identity_params # Create a new identity for this user else user.identities.create identity_params end # Finally we want to create a new auth token pair for the user issue_tokens # Render the user object and the auth tokens render_json data: { token: { access_token: access_token, refresh_token: refresh_token }, user: UserBlueprint.render_as_json(user) }, status: :created else # Render an error saying the provider is not supported render_unsupported_provider_error end end def set_identity @identity = Identity.where(provider: provider, uid: external.info.uid).first end def set_user # The user is already logged in (access_token present in request) # We want to set the user to the current user if current_user.present? @user = current_user # The user has signed in with this provider before # Return the existing account elsif identity.present? @user = identity.user # There is an existing user account with the same email & the user is not # logged in. We don't want to create an account for safety. # Tell the user to sign in to their account and link it there elsif User.where(email: external.info.email).any? render_existing_account_error provider: provider return false # The user has never signed in before, we want to create a new account from # the external info object else @user = User.create_from_provider_info(external.info) end true end # We want to update the identity object with the new uid and provider. # Storing the ccess_token or refresh_token is optional since that # can be delt with on the native clients. def identity_params { provider: external.info.provider, uid: external.info.uid, access_token: external.credentials.access_token, refresh_token: external.credentials.refresh_token, auth_hash: external.info.to_h } end def issue_tokens @access_token, @refresh_token = Jwt::Issuer.call user response.set_header 'Access-token', access_token response.set_header 'Refresh-token', refresh_token end private # Returns the External::Provider object for the current provider # We can access the user info object and raw_info hash from here def external @external ||= External::Provider::Base .for(provider) .new(create_params[:token]) end def create_params params.require(:data).permit(:token) end end