# Sign-up and Login via custom API - Will it work properly?

**URL:** https://meta.discourse.org/t/sign-up-and-login-via-custom-api-will-it-work-properly/166912
**Category:** Development
**Tags:** rest-api
**Created:** [October 11, 2020, 6:59pm UTC](https://meta.discourse.org/t/sign-up-and-login-via-custom-api-will-it-work-properly/166912 "2020-10-11T18:59:20Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![Stefan\_Brighiu\_M](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/stefan_brighiu_m/32/174499_2.png) [@Stefan\_Brighiu\_M](https://meta.discourse.org/u/Stefan_Brighiu_M)
#### Post date: [October 11, 2020, 6:59pm UTC](https://meta.discourse.org/t/sign-up-and-login-via-custom-api-will-it-work-properly/166912/1 "2020-10-11T18:59:20Z")

</div>

In our case, we have a need to just use Discourse as a backend only (+ more or less like a CMS).

I’ve exposed the routes for sign-up and log-in using:

> skip\_before\_action :redirect\_to\_login\_if\_required, raise: false  
> skip\_before\_action :verify\_authenticity\_token, raise: false

… and allowed the routes in api\_parameter\_allowed?. In the final version i will do a salt of some kind just to add a bit of security, but this is only a draft.

… and used the code below.

All I want to know is if I missed anything. In the future we will probably login with google, maybe apple, and this flow would allow me to not open a browser at all for the sign up with email and login with email flows.

---

<div class="post-metadata">

### Author: ![Stefan\_Brighiu\_M](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/stefan_brighiu_m/32/174499_2.png) [@Stefan\_Brighiu\_M](https://meta.discourse.org/u/Stefan_Brighiu_M)
#### Post date: [October 11, 2020, 7:44pm UTC](https://meta.discourse.org/t/sign-up-and-login-via-custom-api-will-it-work-properly/166912/2 "2020-10-11T19:44:55Z")

</div>

I’ve figured out how to do the sign up and login all via API and hopefully everything is fine.

I use this to create a user

```begin
      normalized_username = User.normalize_username(username)
    rescue
      normalized_username = nil
    end
    raise PluginError.new(:invalid_signup_username) if !normalized_username.present? || User.username_exists?(normalized_username) || User.reserved_username?(normalized_username)

    # Try like this
    raise PluginError.new(:invalid_signup_email) if (existing_user = User.find_by_email(email))

    begin
      new_user = User.new
      new_user.name = first_name
      new_user.email = email
      new_user.password = password
      new_user.username = username
      new_user.active = false
      new_user.approved = true
      new_user.approved_by_id = -1
      new_user.approved_at = DateTime.now

      new_user.password_required!

      new_user.save

      new_user.email_tokens.create(email: email) unless new_user.email_tokens.active.exists?
      if (email_token = new_user.email_tokens.active.where(email: email)&.first)
        EmailToken.confirm(email_token.token, skip_reviewable: true)
      end

      new_user.update!(active: true)
    rescue => e
      raise e
    end

```

I use this to sign up a user

```plaintext
new_user = self.class.commit_user_to_db(first_name, email, password, username)

    raise PluginError.new(:error_creating_user_discourse) unless new_user.present? && new_user.is_a?(User) && new_user.approved?

    user_api_key = UserApiKey.create!(
      application_name: "#{application_name}-v#{version}",
      client_id: client_id,
      user_id: new_user.id,
      push_url: "https://api.discourse.org/api/publish_#{platform === Constants::PLATFORMS[:ios] ? 'ios' : ''}#{platform === Constants::PLATFORMS[:android] ? 'android' : ''}",
      scopes: %w[notifications session_info read write message_bus push]
    )

```

I get the user api key using

```plaintext
    email = params[:email]&.to_s
    password = params[:password]&.to_s

    return render json: PluginError.new(:invalid_login_email) unless email.present?
    return render json: PluginError.new(:invalid_login_password) unless password.present?

    user = User.joins(:user_emails).where({ user_emails: { email: email } })&.first
    return render json: PluginError.new(:invalid_login_email) unless user.present?

    return render json: PluginError.new(:invalid_login_password) unless user.confirm_password?(password)

    #destroy any old keys we had for this client_id
    UserApiKey.where(user_id: user.id, client_id: client_id).destroy_all

    user_api_key = UserApiKey.create!(
      application_name: "#{application_name}-v#{version}",
      client_id: client_id,
      user_id: user.id,
      push_url: "https://api.discourse.org/api/publish_#{platform === Constants::PLATFORMS[:ios] ? 'ios' : ''}#{platform === Constants::PLATFORMS[:android] ? 'android' : ''}",
      scopes: %w[notifications session_info read write message_bus push]
    )

```

Is this ok from discourse POV?  
Our users will never go on the website, and we want to hide it from all places.
