sei-ec-remote / project-4-issues

Open an issue to receive help on project 4
0 stars 0 forks source link

Change Password Request Keeps Throwing 401 - Unauthorized Error #38

Closed davidnierman closed 2 years ago

davidnierman commented 2 years ago

What stack are you using?

(ex: MERN(mongoose + react), DR(django + react), PEN, etc.)

DR

What's the problem you're trying to solve?

Change Password Request Keeps Throwing 401 - Unauthorized Error

Post any code you think might be relevant (one fenced block per file)

Client Side


App.js

          <Route
            path='/change-password'
            element={
              <RequireAuth user={user}>
                <ChangePassword msgAlert={msgAlert} user={user} />
              </RequireAuth>}
          />

ChangePassword.js

const ChangePassword = (props) => {
    // constructor(props) {
    //  super(props)

    //  this.state = {
    //      oldPassword: '',
    //      newPassword: '',
    //  }
    // }
    const [oldPassword, setOldPassword] = useState('')
    const [newPassword, setNewPassword] = useState('')

    const navigate = useNavigate()

    const onChangePassword = (event) => {
        event.preventDefault()

        const { msgAlert, user } = props
        console.log('the user', user)

        const passwords = {oldPassword, newPassword}

        changePassword(passwords, user)
            .then(() =>
                msgAlert({
                    heading: 'Change Password Success',
                    message: messages.changePasswordSuccess,
                    variant: 'success',
                })
            )
            .then(() => navigate('/'))
            .catch((error) => {
                console.log("PASSWORDS: ", passwords)
                console.log("USER", user)
                console.log("ERROR: ", error)
                setOldPassword('')
                setNewPassword('')
                msgAlert({
                    heading: 'Change Password Failed with error: ' + error.message,
                    message: messages.changePasswordFailure,
                    variant: 'danger',
                })
            })
    }

    return (
        <div className='row'>
            <div className='col-sm-10 col-md-8 mx-auto mt-5'>
                <h3>Change Password</h3>
                <Form onSubmit={onChangePassword}>
                    <Form.Group controlId='oldPassword'>
                        <Form.Label>Old password</Form.Label>
                        <Form.Control
                            required
                            name='oldPassword'
                            value={oldPassword}
                            type='password'
                            placeholder='Old Password'
                            onChange={e => setOldPassword(e.target.value)}
                        />
                    </Form.Group>
                    <Form.Group controlId='newPassword'>
                        <Form.Label>New Password</Form.Label>
                        <Form.Control
                            required
                            name='newPassword'
                            value={newPassword}
                            type='password'
                            placeholder='New Password'
                            onChange={e => setNewPassword(e.target.value)}
                        />
                    </Form.Group>
                    <Button variant='primary' type='submit'>
                        Submit
                    </Button>
                </Form>
            </div>
        </div>
    )
}

auth.js

// somehow this is not sending authorization over correctly
export const changePassword = (passwords, user) => {
    return axios({
        url: apiUrl + '/change-password',
        method: 'PATCH',
        headers: {
            Authorization: `Token token=${user.token}`,
        },
        data: {
            passwords: {
                old: passwords.oldPassword,
                new: passwords.newPassword,
            },
        },
    })
}

Server Side


urls.py

 path('change-password', ChangePassword.as_view(), name='change-pw')

user_views.py

class ChangePassword(generics.UpdateAPIView): 
    def partial_update(self, request):
        print('REQUEST AUTH:', request.auth)
        print("PARTIALLY UPDATING!")
        user = request.user
        # print('THIS IS THE USER:', user.token) #interesting user does not have token
        # Pass data through serializer
        data=request.data
        print('HERE IS THE PASSWORD DATA BEING PASSED: ', data)
        serializer = ChangePasswordSerializer(data=request.data['passwords'])
        print("PASSWORD SERIALIZER: ", serializer)
        if serializer.is_valid():
            # This is included with the Django base user model
            # https://docs.djangoproject.com/en/3.1/ref/contrib/auth/#django.contrib.auth.models.User.check_password
            if not user.check_password(serializer.data['old']):
                return Response({ 'msg': 'Wrong password' }, status=status.HTTP_422_UNPROCESSABLE_ENTITY)

            # set_password will also hash the password
            # https://docs.djangoproject.com/en/3.1/ref/contrib/auth/#django.contrib.auth.models.User.set_password
            user.set_password(serializer.data['new'])
            user.save()

            return Response(status=status.HTTP_204_NO_CONTENT)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

serializer.py

class ChangePasswordSerializer(serializers.Serializer):
    model = get_user_model()
    old = serializers.CharField(required=True)
    new = serializers.CharField(required=True)

If you see an error message, post it here. If you don't, what unexpected behavior are you seeing?

from console.log client side: xhr.js:210 PATCH http://localhost:8000/change-password 401 (Unauthorized)

from server side CLI/TERMINAL: Unauthorized: /change-password [27/Apr/2022 05:21:03] "PATCH /change-password HTTP/1.1" 401 27

What is your best guess as to the source of the problem?

I have no idea. I pulled the whole request apart from the data to the user and auth...

Here is what I know:

  1. The request works in Postman. The request does not work within the browser.. --> I think this rules out that it is an server/api issue

  2. If I take the token from the browser login session and use it in Postman it works --> I think this rules out it is token issue

  3. For some reason the 401 error is thrown BEFORE the rest of user_views.py's class ChangePassword(generics.UpdateAPIView): and its function def partial_update(self, request): is ran. --> I know this based on adding print() statements

What things have you already tried to solve the problem?

I have console.logged and printed everything I could possibly think of.

I have looked up django documentation for the class that is inherited by ChangePassword class in user_views: updateapiview - > I am convinced it is not api/servicer side since it works on postman, but looked this up anyways

I have checked my serializer too to cross my t's and dot my i's

On the client side I have compared the axios request to the other user/auth requests and tested the other ones. All of them work except updatepassword..

Paste a link to your repository here

composting-mgmt-systems composting-mgmt-systems-api

timmshinbone commented 2 years ago

change this line:

Authorization: `Token token=${user.token}`

to this:

Authorization: `Token ${user.token}`

inside your changePassword function.

davidnierman commented 2 years ago

Wow that was it! - so why do my other requests work using the original way? - such as signout

export const signOut = (user) => {
    return axios({
        url: apiUrl + '/sign-out/',
        method: 'DELETE',
        headers: {
            Authorization: `Token token=${user.token}`,
        },
    })
}

// somehow this is not sending authorization over correctly
export const changePassword = (passwords, user) => {
    return axios({
        url: apiUrl + '/change-password',
        method: 'PATCH',
        headers: {
            Authorization: `Token ${user.token}`,
        },
        data: {
            passwords: {
                old: passwords.oldPassword,
                new: passwords.newPassword,
            },
        },
    })
}
timmshinbone commented 2 years ago

Are you certain that the signOut is working? because it shouldn't be reading that token, change it to the other syntax.

What broke the sign in route and how? you don't need to pass a user to sign in since you don't need the token

davidnierman commented 2 years ago

turns out the sign in route broke because I accidentally typed something and saved it when I thought I was in a different screen. git diff helped me figure that out.

But yeah the signout route works with this Authorization:Token token=${user.token},

timmshinbone commented 2 years ago

it shouldn't so that should be changed