Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return credentials input fields in getProviders #3130

Closed
jasonjei opened this issue Nov 5, 2021 · 3 comments
Closed

Return credentials input fields in getProviders #3130

jasonjei opened this issue Nov 5, 2021 · 3 comments
Labels
enhancement New feature or request good first issue Good issue to take for first time contributors stale Did not receive any activity for 60 days

Comments

@jasonjei
Copy link

jasonjei commented Nov 5, 2021

Description 📓

I want to be able to get my credentials providers input fields using the Client API from /api/auth/providers. I'm not able to do that with import { getProviders } from "next-auth/react" in 4.0.0-beta.6

The providers client API doesn't give the same fields as the default sign-in page providers object. I don't get access to providers["credentials"].credentials object like the vendor-provided sign-in page does. Would it be possible to fill in an extra key in the providers["credentials"].credentials object in the /api/auth/providers API to supply me the input fields required so that I can dynamically create the credentials input fields instead of hard-coding them?

How to reproduce ☕️

What I currently get from /api/auth/providers:

{"credentials":{"id":"credentials","name":"Credentials","type":"credentials","signinUrl":"https://dev/api/auth/signin/credentials","callbackUrl":"https://dev/api/auth/callback/credentials"},"github":{"id":"github","name":"GitHub","type":"oauth","signinUrl":"https://dev/api/auth/signin/github","callbackUrl":"https://dev/api/auth/callback/github"},"facebook":{"id":"facebook","name":"Facebook","type":"oauth","signinUrl":"https://dev/api/auth/signin/facebook","callbackUrl":"https://dev/api/auth/callback/facebook"}}

What I would like to get:

{
  "credentials": {
    "id": "credentials",
    "name": "Credentials",
    "type": "credentials",
    "signinUrl": "https://dev/api/auth/signin/credentials",
    "callbackUrl": "https://dev/api/auth/callback/credentials",
    "credentials": {
      "email": {
        "label": "Email",
        "type": "text",
        "placeholder": "email"
      },
      "password": {
        "label": "Password",
        "type": "password"
      }
    }
  },
  "github": {
    "id": "github",
    "name": "GitHub",
    "type": "oauth",
    "signinUrl": "https://dev/api/auth/signin/github",
    "callbackUrl": "https://dev/api/auth/callback/github"
  },
  "facebook": {
    "id": "facebook",
    "name": "Facebook",
    "type": "oauth",
    "signinUrl": "https://dev/api/auth/signin/facebook",
    "callbackUrl": "https://dev/api/auth/callback/facebook"
  }
}

What I want to do in my custom signin page:

import React, { useState, useEffect } from 'react'
import { getCsrfToken, getProviders, useSession } from "next-auth/react"
import { useRouter } from "next/router";
import { Alert, Col, Container, Row } from 'react-bootstrap';

export default function signin() {
  const [providers, setProviders] = useState(null);
  const [csrfToken, setCsrfToken] = useState(null);
  const router = useRouter()
  const { query } = useRouter();
  const { data: session, status } = useSession()

  useEffect(async() => {
    if (status === 'authenticated') {
      router.push('/');
    }

    if (providers === null)
      setProviders(await getProviders());
    if (csrfToken === null)
      setCsrfToken(await getCsrfToken());
    
    return () => {
      // cleanup
    }
  });

  return (
    <>
      {(<div className="signin">
        {query.error && (
          <div className="error">
            <p>{query.error}</p>
          </div>
        )}
        {Object.keys(providers).map((providerKey, i) => (
          <div key={providers[providerKey].id} className="provider">
            {providers[providerKey].type === "oauth" && (
              <form action={providers[providerKey].signinUrl} method="POST">
                <input type="hidden" name="csrfToken" value={csrfToken} />
                {callbackUrl && (
                  <input type="hidden" name="callbackUrl" value={callbackUrl} />
                )}
                <button type="submit" className="button">
                  Sign in with {providers[providerKey].name}
                </button>
              </form>
            )}
            {(providers[providerKey].type === "email" || providers[providerKey].type === "credentials") &&
              i > 0 &&
              Object.keys(providers)[i - 1].type !== "email" &&
              Object.keys(providers)[i - 1].type !== "credentials" && <hr />}
            {providers[providerKey].type === "email" && (
              <form action={providers[providerKey].signinUrl} method="POST">
                <input type="hidden" name="csrfToken" value={csrfToken} />
                <label for={`input-email-for-${providers[providerKey].id}-provider`}>
                  Email
                </label>
                <input
                  id={`input-email-for-${providers[providerKey].id}-provider`}
                  autoFocus
                  type="text"
                  name="email"
                  value={email}
                  placeholder="email@example.com"
                />
                <button type="submit">Sign in with {providers[providerKey].name}</button>
              </form>
            )}
            {providers[providerKey].type === "credentials" && (
              <form action={providers[providerKey].callbackUrl} method="POST">
                <input type="hidden" name="csrfToken" value={csrfToken} />
                {Object.keys(providers[providerKey].credentials).map((credential) => {
                  return (
                    <div key={`input-group-${providers[providerKey].id}`}>
                      <label
                        for={`input-${credential}-for-${providers[providerKey].id}-provider`}
                      >
                        {provider.credentials[credential].label || credential}
                      </label>
                      <input
                        name={credential}
                        id={`input-${credential}-for-${providers[providerKey].id}-provider`}
                        type={providers[providerKey].credentials[credential].type || "text"}
                        value={providers[providerKey].credentials[credential].value || ""}
                        placeholder={
                          providers[providerKey].credentials[credential].placeholder || ""
                        }
                      />
                    </div>
                  )
                })}
                <button type="submit">Sign in with {providers[providerKey].name}</button>
              </form>
            )}
            {(providers[providerKey].type === "email" || providers[providerKey].type === "credentials") &&
              i + 1 < providersToRender.length && <hr />}
          </div>
        ))}
      </div>)}
    </>
  )
}

What I have to resort doing--hard-coding in my credentials fields:

        <form action={props.providers['credentials'].callbackUrl} method="post" onSubmit={handleSubmit}>
          <input type="hidden" name="csrfToken" value={props.csrfToken} />
          <div className="form-group">
            <Form.Label>Email Address</Form.Label>
            <Form.Control type="email" name="email" placeholder="name@address.com" />
          </div>
          <div className="form-group">
            <Row>
              <Col>
                <Form.Label>Password</Form.Label>
              </Col>
              <Col xs="auto">
                <Link href="/password-reset" passHref>
                  <Form.Text as="a" className="small text-muted">
                    Forgot password?
                  </Form.Text>
                </Link>
              </Col>
            </Row>
            {/* Have to use 2 input groups because it messes with Chome password mgr */}
            { showPassword && (
              <InputGroup className="input-group-merge">
                <Form.Control name="password" type="input" defaultValue={password} placeholder="Enter your password" />
                <InputGroup.Text onClick={() => togglePassword()}>
                  <FeatherIcon icon="eye" size="1em" />
                </InputGroup.Text>
              </InputGroup>
            )}
            { !showPassword && (
              <InputGroup className="input-group-merge">
                <Form.Control name="password" type="password" defaultValue={password} placeholder="Enter your password" />
                <InputGroup.Text onClick={() => togglePassword()}>
                  <FeatherIcon icon="eye" size="1em" />
                </InputGroup.Text>
              </InputGroup>
            )}
          </div>
          <Button type="submit" size="lg" className="w-100 mb-3">
            Sign in
          </Button>
        </form>

Contributing 🙌🏽

Yes, I am willing to help implement this feature in a PR

@jasonjei jasonjei added the enhancement New feature or request label Nov 5, 2021
@balazsorban44
Copy link
Member

although I think the second option is way cleaner, but it looks like our default signin page indeed takes advantage of having access to the full provider configuration

https://github.com/nextauthjs/next-auth/blob/beta/src/core/pages/signin.tsx

so feel free to work on this

@balazsorban44 balazsorban44 added the good first issue Good issue to take for first time contributors label Nov 6, 2021
@balazsorban44 balazsorban44 changed the title /api/auth/providers Client API doesn't supply credentials required credentials (email, password, etc) Return credentials input fields in getProviders Nov 6, 2021
@stale
Copy link

stale bot commented Jan 5, 2022

Hi there! It looks like this issue hasn't had any activity for a while. It will be closed if no further activity occurs. If you think your issue is still relevant, feel free to comment on it to keep it open. (Read more at #912) Thanks!

@stale stale bot added the stale Did not receive any activity for 60 days label Jan 5, 2022
@stale
Copy link

stale bot commented Jan 12, 2022

Hi there! It looks like this issue hasn't had any activity for a while. To keep things tidy, I am going to close this issue for now. If you think your issue is still relevant, just leave a comment and I will reopen it. (Read more at #912) Thanks!

@stale stale bot closed this as completed Jan 12, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good issue to take for first time contributors stale Did not receive any activity for 60 days
Projects
None yet
Development

No branches or pull requests

2 participants