Skip to content
This repository was archived by the owner on Dec 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion examples/dotnet/Examples/RealmTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using dotnet;
using MongoDB.Bson;
Expand Down Expand Up @@ -84,8 +86,79 @@ public async System.Threading.Tasks.Task ModifiesATask()
return;
}

[Test]
public async System.Threading.Tasks.Task LogsOnManyWays()
{
// :code-block-start: logon_anon
Realms.Sync.User anonUser = await app.LogInAsync(Credentials.Anonymous());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if that's the style of the docs, but I feel that using the fully qualified name is overly verbose and doesn't convey much useful information. It's also not very copy-pasteable as in my experience most projects use vars for variables and a few use the type name (without the namespace).

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nirinchev - the reason I had the FQDN here was because we have a user-defined class called "User", so the compiler complained. I've updated the custom class names because they are semi-arbitrary.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestion would be to always use var as the variable name should be indicative enough. E.g. anonUser is obviously a User, so explicitly specifying the type is redundant. This also lets you avoid some of the type collisions.

// :code-block-end:
Assert.AreEqual(UserState.LoggedIn, anonUser.State);
await anonUser.LogOutAsync();
// :code-block-start: logon_EP
Realms.Sync.User emailUser = await app.LogInAsync(Credentials.EmailPassword("caleb@mongodb.com", "shhhItsASektrit!"));
// :code-block-end:
Assert.AreEqual(UserState.LoggedIn, emailUser.State);
await emailUser.LogOutAsync();
// :code-block-start: logon_API
Realms.Sync.User apiUser = await app.LogInAsync(Credentials.ApiKey("eRECwv1e6gkLEse99XokWOgegzoguEkwmvYvXk08zAucG4kXmZu7TTgV832SwFCv"));
// :code-block-end:
Assert.AreEqual(UserState.LoggedIn, apiUser.State);
await apiUser.LogOutAsync();
// :code-block-start: logon_Function
var functionParameters = new Dictionary<string, string>()
{
{ "username", "caleb" },
{ "password", "shhhItsASektrit!" },
{ "someOtherProperty", "cheesecake" }
};
Realms.Sync.User functionUser =
await app.LogInAsync(Credentials.Function(functionParameters));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will work, but the code can be simplified by using anonymous objects:

var functionParameters = new
{
    username = "caleb",
    password = "shhhItsASektrit",
    someOtherProperty = "cheesecake"
};
var functionUser = await app.LogInAsync(Credentials.Function(functionParameters));

Note that when using the anonymous object syntax, you can also hint that properties don't have to be strings only. Any serializable .NET type, including embedded objects will work:

var functionParameters = new
{
    biometricsValidated = false,
    todaysDate = DateTime.UtcNow
}

Of course, don't have to go overboard with the extra properties, just FYI.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

niiiiice

// :code-block-end:
Assert.AreEqual(UserState.LoggedIn, functionUser.State);
await functionUser.LogOutAsync();
// :code-block-start: logon_JWT
Realms.Sync.User jwtUser =
await app.LogInAsync(Credentials.JWT("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkNhbGViIiwiaWF0IjoxNjAxNjc4ODcyLCJleHAiOjI1MTYyMzkwMjIsImF1ZCI6InR1dHMtdGlqeWEifQ.LHbeSI2FDWrlUVOBxe-rasuFiW-etv2Gu5e3eAa6Y6k"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to call it <JWT_token> similar to the fb and google examples? And is this a real credential that should be removed prior to commit?

// :code-block-end:
Assert.AreEqual(UserState.LoggedIn, jwtUser.State);
await jwtUser.LogOutAsync();
try
{
// :code-block-start: logon_fb
Realms.Sync.User fbUser =
await app.LogInAsync(Credentials.Facebook("<facebook_token>"));
// :code-block-end:
}
catch (Exception e)
{
Assert.AreEqual("http error code considered fatal: Client Error: 401", e.Message);
}
try
{
// :code-block-start: logon_google
Realms.Sync.User googleUser =
await app.LogInAsync(Credentials.Google("<google_token>"));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're calling this google auth code now as using the access token flow doesn't work.

// :code-block-end:
}
catch (Exception e)
{
Assert.AreEqual("http error code considered fatal: Client Error: 401", e.Message);
}
try
{
// :code-block-start: logon_apple
Realms.Sync.User appleUser =
await app.LogInAsync(Credentials.Apple("<apple_token>"));
// :code-block-end:
}

catch (Exception e)
{
Assert.AreEqual("http error code considered fatal: Client Error: 401", e.Message);
}
}

[TearDown]
[TearDown]
public async System.Threading.Tasks.Task TearDown()
{
config = new SyncConfiguration("My Project", user);
Expand Down
26 changes: 13 additions & 13 deletions examples/dotnet/dotnet.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples", "Examples\Examples.csproj", "{C97249A2-33A1-457A-AAEC-389B62984885}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples", "Examples\Examples.csproj", "{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -13,17 +13,17 @@ Global
Release|iPhone = Release|iPhone
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C97249A2-33A1-457A-AAEC-389B62984885}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Release|Any CPU.Build.0 = Release|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Debug|iPhone.Build.0 = Debug|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Release|iPhone.ActiveCfg = Release|Any CPU
{C97249A2-33A1-457A-AAEC-389B62984885}.Release|iPhone.Build.0 = Release|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Release|Any CPU.Build.0 = Release|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Debug|iPhone.ActiveCfg = Debug|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Debug|iPhone.Build.0 = Debug|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Release|iPhone.ActiveCfg = Release|Any CPU
{C7CFE106-78AB-4B53-A1F1-1008EAD4FA5C}.Release|iPhone.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
161 changes: 98 additions & 63 deletions source/dotnet/authenticate.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,115 +15,150 @@ Authenticate a User
Overview
--------

.. _dotnet-login:
{+service+} provides an API for authenticating users using any enabled
authentication provider. Instantiate a ``Credentials`` object and pass
it to the ``app.login()`` method to authenticate a user and create a ``User``

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The .NET method is LogInAsync - I don't think anyone would get confused over it, but it's always better to use the exact method names.

"to authenticate a user and create a User object" sounds a bit repetitive. Additionally, the "create a User" wording is a bit imprecise as it implies constructing the instance as opposed to obtaining a reference to it by calling a method. Don't have suggestions for avoiding the repetition, but "create a User object" can probably be reworded as "obtain a User instance".

object. Each authentication provider corresponds to a method used to
instantiate ``Credentials`` objects using that authentication provider.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds a bit heavy - perhaps if we inverted the flow, it would be easier to follow. I.e. something like "The Credentials class exposes factory methods for each authentication provider."

See the table below to find the method that instantiates the
``Credentials`` instance for your authentication provider:

.. list-table::
:header-rows: 1
:widths: 50 50

* - Authentication Provider
- Credentials Generation Method

* - :ref:`Anonymous <dotnet-login-anonymous>`
- ``Credentials.Anonymous()``

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice if those linked to the API docs for that method as those typically contain valuable information about parameters, return types, etc.

I can send you a .zip containing the API docs for the v10 SDK if that would help.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please...or a link would be fine, too.


* - :ref:`Email/Password <dotnet-login-email-password>`
- ``Credentials.EmailPassword(email, password)``

* - :ref:`API Key <dotnet-login-api-key>`
- ``Credentials.ApiKey(userAPIKey)``

* - :ref:`Custom Function <dotnet-login-custom-function>`
- ``Credentials.Function(functionPayload)``

* - :ref:`Custom JWT <dotnet-login-custom-jwt>`
- ``Credentials.JWT(jwt)``

* - :ref:`Google OAuth <dotnet-login-google>`
- ``Credentials.Google(googleAuthCode)``

* - :ref:`Facebook OAuth <dotnet-login-facebook>`
- ``Credentials.Facebook(facebookToken)``

* - :ref:`Sign-in With Apple <dotnet-login-apple>`
- ``Credentials.Apple(appleToken)``


Before you can authenticate a user, ensure you have:

- :ref:`Created a {+app+} <create-a-realm-app>`
- Enabled one or more :ref:`authentication providers <authentication-providers>`
- :ref:`Installed the .NET SDK <dotnet-install>`


Log In
------

.. _dotnet-login-anonymous:

Anonymous
~~~~~~~~~
Anonymous Authentication
~~~~~~~~~~~~~~~~~~~~~~~~

.. tabs-realm-languages::
.. tab::
:tabid: c-sharp
.. code-block:: csharp
If you have enabled :ref:`Anonymous authentication <anonymous-authentication>` in the
{+ui+}, users can immediately log into your app without providing any identifying
information. The following code shows how to do this:

.. literalinclude:: /examples/generated/code/start/RealmTests.codeblock.logon_anon.cs
:language: csharp

.. _dotnet-login-email-password:

Email/Password
~~~~~~~~~~~~~~
Email/Password Authentication
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. tabs-realm-languages::

.. tab::
:tabid: c-sharp

.. code-block:: csharp
If you have enabled :ref:`Email/Password authentication <email-password-authentication>`,
you can log in using the following code:

.. literalinclude:: /examples/generated/code/start/RealmTests.codeblock.logon_EP.cs
:language: csharp

.. _dotnet-login-api-key:

API Key
~~~~~~~
If you have enabled :ref:`API Key authentication <api-key-authentication>`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since you've added Authentication to Anonymous and Email/Password headers, should you also update the rest of the authn method headers?

you can log in using the following code:

.. tabs-realm-languages::

.. tab::
:tabid: c-sharp

.. code-block:: csharp
.. literalinclude:: /examples/generated/code/start/RealmTests.codeblock.logon_API.cs
:language: csharp

.. _dotnet-login-custom-function:

Custom Function
~~~~~~~~~~~~~~~
If you have enabled the

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: I think we prefer past tense over past perfect. E.g. "If you enabled ..." instead of "If you have enabled..." Applies to all instances.

:doc:`Custom Function authentication provider </authentication/custom-function>`,
you can log in using the following code:

.. tabs-realm-languages::

.. tab::
:tabid: c-sharp

.. code-block:: csharp
.. literalinclude:: /examples/generated/code/start/RealmTests.codeblock.logon_Function.cs
:language: csharp

.. _dotnet-login-custom-jwt:

Custom JWT
~~~~~~~~~~
If you have enabled the :doc:`Custom JWT authentication provider </authentication/custom-jwt>`,
you can log in using the following code:

.. tabs-realm-languages::

.. tab::
:tabid: c-sharp

.. code-block:: csharp
.. literalinclude:: /examples/generated/code/start/RealmTests.codeblock.logon_JWT.cs
:language: csharp

.. _dotnet-login-facebook:

Facebook OAuth
~~~~~~~~~~~~~~
Facebook Authentication
~~~~~~~~~~~~~~~~~~~~~~~

.. tabs-realm-languages::

.. tab::
:tabid: c-sharp

.. code-block:: csharp
If you have enabled :ref:`Facebook authentication <facebook-authentication>`,
you can log in using the following code:

.. literalinclude:: /examples/generated/code/start/RealmTests.codeblock.logon_fb.cs
:language: csharp

.. _dotnet-login-google:

Google OAuth
~~~~~~~~~~~~
Google Authentication
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. tabs-realm-languages::

.. tab::
:tabid: c-sharp

.. code-block:: csharp
If you have enabled :ref:`Google authentication <google-authentication>`,
you can log in using the following code:

.. literalinclude:: /examples/generated/code/start/RealmTests.codeblock.logon_google.cs
:language: csharp

.. _dotnet-login-apple:

Sign-in with Apple
~~~~~~~~~~~~~~~~~~
If you have enabled :ref:`Sign-in with Apple authentication <apple-id-authentication>`,
you can log in using the following code:

.. tabs-realm-languages::

.. tab::
:tabid: c-sharp

.. code-block:: csharp
.. literalinclude:: /examples/generated/code/start/RealmTests.codeblock.logon_apple.cs
:language: csharp

.. _dotnet-logout:

Log Out
-------

.. tabs-realm-languages::
.. tab::
:tabid: c-sharp
.. code-block:: csharp
Once logged in, you can log out by calling the ``LogOutAsync()`` method:

.. literalinclude:: /examples/generated/code/start/RealmTests.codeblock.logout.cs
:language: csharp

.. include:: /includes/log-out-queries-in-progress.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
const string appId = "tuts-tijya";
app = Realms.Sync.App.Create(appId);
app = Realms.Sync.App.Create(myRealmAppId);
Loading